mirror of https://codeberg.org/pzp/pzp-db.git
implement powers in identity.add()
This commit is contained in:
parent
e6aa33d3f0
commit
557ea2252c
99
lib/index.js
99
lib/index.js
|
@ -23,6 +23,7 @@ const { decrypt } = require('./encryption')
|
|||
* @typedef {import('ppppp-keypair').KeypairPrivateSlice} KeypairPrivateSlice
|
||||
* @typedef {import('./msg-v3').Msg} Msg
|
||||
* @typedef {import('./msg-v3').IdentityData} IdentityData
|
||||
* @typedef {import('./msg-v3').IdentityPower} IdentityPower
|
||||
* @typedef {import('./encryption').EncryptionFormat} EncryptionFormat
|
||||
*
|
||||
* @typedef {Buffer | Uint8Array} B4A
|
||||
|
@ -274,18 +275,23 @@ function initDB(peer, config) {
|
|||
// Or even better, a bloom filter. If you just want to answer no/perhaps.
|
||||
let rec
|
||||
if ((rec = getRecord(msgHash))) return cb(null, rec)
|
||||
else rec = { msg, hash: msgHash }
|
||||
|
||||
// TODO: optimize this. This may be slow if you're adding many msgs in a
|
||||
// row, because it creates a new Map() each time. Perhaps with QuickLRU
|
||||
const tangle = new DBTangle(tangleRootHash, records())
|
||||
|
||||
// Find which pubkeys are authorized to sign this msg given the identity:
|
||||
const pubkeys = new Set()
|
||||
if (msg.metadata.identity && msg.metadata.identity !== IDENTITY_SELF) {
|
||||
const identityTangle = new DBTangle(msg.metadata.identity, records())
|
||||
if (!identityTangle.has(msg.metadata.identity)) {
|
||||
const identity = getIdentityId(rec)
|
||||
let identityTangle = /** @type {DBTangle | null} */ (null)
|
||||
if (identity && !MsgV3.isRoot(msg)) {
|
||||
identityTangle = new DBTangle(identity, records())
|
||||
if (!identityTangle.has(identity)) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('add() failed because the identity tangle is unknown'))
|
||||
}
|
||||
// TODO: prune the identityTangle beyond msg.metadata.identityTips
|
||||
for (const msgHash of identityTangle.topoSort()) {
|
||||
const msg = get(msgHash)
|
||||
if (!msg?.data) continue
|
||||
|
@ -303,6 +309,22 @@ function initDB(peer, config) {
|
|||
return cb(new Error('add() failed msg validation', { cause: err }))
|
||||
}
|
||||
|
||||
// Identity tangle related validations
|
||||
if (msg.metadata.identity === IDENTITY_SELF && identityTangle) {
|
||||
/** @type {IdentityData} */
|
||||
const data = msg.data
|
||||
if (data.action === 'add') {
|
||||
// Does this msg.pubkey have the "add" power?
|
||||
const keypair = { curve: 'ed25519', public: msg.pubkey }
|
||||
const powers = getIdentityPowers(identityTangle, keypair)
|
||||
if (!powers.has('add')) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('add() failed because this msg.pubkey does not have "add" power'))
|
||||
}
|
||||
}
|
||||
// TODO validate 'del'
|
||||
}
|
||||
|
||||
logAppend(msgHash, msg, (err, rec) => {
|
||||
if (err) return cb(new Error('add() failed in the log', { cause: err }))
|
||||
onRecordAdded.set(rec)
|
||||
|
@ -478,6 +500,30 @@ function initDB(peer, config) {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DBTangle} identityTangle
|
||||
* @param {KeypairPublicSlice} keypair
|
||||
* @returns {Set<IdentityPower>}
|
||||
*/
|
||||
function getIdentityPowers(identityTangle, keypair) {
|
||||
const powers = new Set()
|
||||
for (const msgHash of identityTangle.topoSort()) {
|
||||
const msg = get(msgHash)
|
||||
if (!msg?.data) continue
|
||||
/** @type {IdentityData} */
|
||||
const data = msg.data
|
||||
if (data.action !== 'add') continue
|
||||
if (data.add.key.algorithm !== keypair.curve) continue
|
||||
if (data.add.key.bytes !== keypair.public) continue
|
||||
if (data.add.powers) {
|
||||
for (const power of data.add.powers) {
|
||||
powers.add(power)
|
||||
}
|
||||
}
|
||||
}
|
||||
return powers
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a consent signature for the given `keypair` (or the implicit
|
||||
* config.keypair) to be added to the given `identity`.
|
||||
|
@ -502,8 +548,15 @@ function initDB(peer, config) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add the given `keypair` (or the implicit config.keypair) to the given
|
||||
* `identity`, authorized by the given `consent` (or implicitly created on the
|
||||
* fly if the `keypair` contains the private key) with the following `powers`
|
||||
* (defaulting to no powers).
|
||||
*
|
||||
* @param {{
|
||||
* identity: string;
|
||||
* powers?: Array<IdentityPower>;
|
||||
* _disobey?: true;
|
||||
* } & ({
|
||||
* keypair: KeypairPublicSlice & {private?: never};
|
||||
* consent: string;
|
||||
|
@ -514,12 +567,13 @@ function initDB(peer, config) {
|
|||
* @param {CB<Rec>} cb
|
||||
*/
|
||||
function addToIdentity(opts, cb) {
|
||||
if (!opts) return cb(new Error('identity.add() requires an `opts`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.identity) return cb(new Error('identity.add() requires a `identity`'))
|
||||
if (!opts.identity) return cb(new Error('identity.add() requires a `identity`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.keypair) return cb(new Error('identity.add() requires a `keypair`'))
|
||||
if (!opts.keypair) return cb(new Error('identity.add() requires a `keypair`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.keypair.public) return cb(new Error('identity.add() requires a `keypair` with `public`'))
|
||||
if (!opts.keypair.public) return cb(new Error('identity.add() requires a `keypair` with `public`'))
|
||||
let consent = /** @type {string} */ (opts.consent)
|
||||
if (typeof opts.consent === 'undefined') {
|
||||
if (opts.keypair.private) {
|
||||
|
@ -528,6 +582,7 @@ function initDB(peer, config) {
|
|||
return cb(new Error('identity.add() requires a `consent`'))
|
||||
}
|
||||
}
|
||||
const obeying = !opts._disobey
|
||||
const addedKeypair = opts.keypair
|
||||
const signingKeypair = config.keypair
|
||||
|
||||
|
@ -535,11 +590,36 @@ function initDB(peer, config) {
|
|||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_IDENTITY_ADD + base58.decode(opts.identity)
|
||||
)
|
||||
if (!Keypair.verify(addedKeypair, signableBuf, consent)) {
|
||||
if (obeying && !Keypair.verify(addedKeypair, signableBuf, consent)) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('identity.add() failed because the consent is invalid'))
|
||||
}
|
||||
|
||||
// Verify powers of the signingKeypair:
|
||||
const identityTangle = new DBTangle(opts.identity, records())
|
||||
if (obeying) {
|
||||
const signingPowers = getIdentityPowers(identityTangle, signingKeypair)
|
||||
if (!signingPowers.has('add')) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('identity.add() failed because the signing keypair does not have the "add" power'))
|
||||
}
|
||||
}
|
||||
|
||||
// Verify input powers for the addedKeypair:
|
||||
if (obeying && opts.powers) {
|
||||
if (!Array.isArray(opts.powers)) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('identity.add() failed because opts.powers is not an array'))
|
||||
}
|
||||
for (const power of opts.powers) {
|
||||
if (power !== 'add' && power !== 'del' && power !== 'box') {
|
||||
// prettier-ignore
|
||||
return cb(new Error(`identity.add() failed because opts.powers contains an unknown power "${power}"`))
|
||||
}
|
||||
// TODO check against duplicates
|
||||
}
|
||||
}
|
||||
|
||||
const identityRoot = get(opts.identity)
|
||||
if (!identityRoot) {
|
||||
// prettier-ignore
|
||||
|
@ -558,12 +638,15 @@ function initDB(peer, config) {
|
|||
consent,
|
||||
},
|
||||
}
|
||||
if (opts.powers) data.add.powers = opts.powers
|
||||
|
||||
// Fill-in tangle opts:
|
||||
const fullOpts = {
|
||||
identity: IDENTITY_SELF,
|
||||
identityTips: null,
|
||||
tangles: populateTangles([opts.identity]),
|
||||
tangles: {
|
||||
[opts.identity]: identityTangle,
|
||||
},
|
||||
keypair: signingKeypair,
|
||||
data,
|
||||
domain: identityRoot.metadata.domain,
|
||||
|
|
|
@ -2,6 +2,9 @@ module.exports = {
|
|||
/** @type {'self'} */
|
||||
IDENTITY_SELF: 'self',
|
||||
|
||||
/** @type {'any'} */
|
||||
IDENTITY_ANY: 'any',
|
||||
|
||||
SIGNATURE_TAG_MSG_V3: ':msg-v3:',
|
||||
SIGNATURE_TAG_IDENTITY_ADD: ':identity-add:',
|
||||
}
|
||||
|
|
|
@ -17,7 +17,12 @@ const {
|
|||
validateMsgHash,
|
||||
} = require('./validation')
|
||||
const Tangle = require('./tangle')
|
||||
const { IDENTITY_SELF, SIGNATURE_TAG_MSG_V3 } = require('./constants')
|
||||
const {
|
||||
IDENTITY_SELF,
|
||||
IDENTITY_ANY,
|
||||
SIGNATURE_TAG_MSG_V3,
|
||||
} = require('./constants')
|
||||
const { isEmptyObject } = require('./util')
|
||||
|
||||
/**
|
||||
* @typedef {import('ppppp-keypair').Keypair} Keypair
|
||||
|
@ -38,7 +43,7 @@ const { IDENTITY_SELF, SIGNATURE_TAG_MSG_V3 } = require('./constants')
|
|||
* metadata: {
|
||||
* dataHash: string | null;
|
||||
* dataSize: number;
|
||||
* identity: string | (typeof IDENTITY_SELF) | null;
|
||||
* identity: string | (typeof IDENTITY_SELF) | (typeof IDENTITY_ANY);
|
||||
* identityTips: Array<string> | null;
|
||||
* tangles: Record<string, TangleMetadata>;
|
||||
* domain: string;
|
||||
|
@ -54,10 +59,13 @@ const { IDENTITY_SELF, SIGNATURE_TAG_MSG_V3 } = require('./constants')
|
|||
* action: 'del', del: IdentityDel
|
||||
* }} IdentityData
|
||||
*
|
||||
* @typedef {'add' | 'del' | 'box'} IdentityPower
|
||||
*
|
||||
* @typedef {{
|
||||
* key: IdentityKey;
|
||||
* nonce?: string;
|
||||
* consent?: string;
|
||||
* powers?: Array<IdentityPower>;
|
||||
* }} IdentityAdd
|
||||
*
|
||||
* @typedef {{
|
||||
|
@ -71,24 +79,18 @@ const { IDENTITY_SELF, SIGNATURE_TAG_MSG_V3 } = require('./constants')
|
|||
* }} SigKey
|
||||
*
|
||||
* @typedef {{
|
||||
* purpose: 'subidentity';
|
||||
* algorithm: 'tangle';
|
||||
* bytes: string;
|
||||
* }} SubidentityKey;
|
||||
*
|
||||
* @typedef {{
|
||||
* purpose: 'box';
|
||||
* algorithm: 'x25519-xsalsa20-poly1305';
|
||||
* bytes: string;
|
||||
* }} BoxKey;
|
||||
*
|
||||
* @typedef {SigKey | SubidentityKey | BoxKey} IdentityKey
|
||||
* @typedef {SigKey | BoxKey} IdentityKey
|
||||
*
|
||||
* @typedef {{
|
||||
* data: any;
|
||||
* domain: string;
|
||||
* keypair: Keypair;
|
||||
* identity: string | null;
|
||||
* identity: string | (typeof IDENTITY_SELF) | (typeof IDENTITY_ANY);
|
||||
* identityTips: Array<string> | null;
|
||||
* tangles: Record<string, Tangle>;
|
||||
* }} CreateOpts
|
||||
|
@ -137,7 +139,7 @@ function create(opts) {
|
|||
if (!opts.tangles) throw new Error('opts.tangles is required')
|
||||
|
||||
const [dataHash, dataSize] = representData(opts.data)
|
||||
const identity = opts.identity ? stripIdentity(opts.identity) : null
|
||||
const identity = opts.identity
|
||||
const identityTips = opts.identityTips ? opts.identityTips.sort() : null
|
||||
|
||||
const tangles = /** @type {Msg['metadata']['tangles']} */ ({})
|
||||
|
@ -238,6 +240,7 @@ function createIdentity(keypair, domain, nonce = getRandomNonce) {
|
|||
bytes: keypair.public,
|
||||
},
|
||||
nonce: typeof nonce === 'function' ? nonce() : nonce,
|
||||
powers: ['add', 'del', 'box'],
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -268,6 +271,13 @@ function fromPlaintextBuffer(plaintextBuf, msg) {
|
|||
return { ...msg, data: JSON.parse(plaintextBuf.toString('utf-8')) }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Msg} msg
|
||||
*/
|
||||
function isRoot(msg) {
|
||||
return isEmptyObject(msg.metadata.tangles)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMsgHash,
|
||||
getMsgId,
|
||||
|
@ -280,6 +290,7 @@ module.exports = {
|
|||
stripIdentity,
|
||||
toPlaintextBuffer,
|
||||
fromPlaintextBuffer,
|
||||
isRoot,
|
||||
Tangle,
|
||||
validate,
|
||||
}
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
const { stripIdentity } = require('./strip')
|
||||
const { isEmptyObject } = require('./util')
|
||||
|
||||
/**
|
||||
* @typedef {import('.').Msg} Msg
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {any} obj
|
||||
*/
|
||||
function isEmptyObject(obj) {
|
||||
for (const _key in obj) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Msg} msg
|
||||
* @param {string | 0} id
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
/**
|
||||
* @param {any} obj
|
||||
*/
|
||||
function isEmptyObject(obj) {
|
||||
for (const _key in obj) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isEmptyObject,
|
||||
}
|
15
protospec.md
15
protospec.md
|
@ -24,7 +24,7 @@ interface Msg {
|
|||
metadata: {
|
||||
dataHash: ContentHash | null // blake3 hash of the `content` object serialized
|
||||
dataSize: number // byte size (unsigned integer) of the `content` object serialized
|
||||
identity: string | 'self' | null // blake3 hash of an identity tangle root msg, or the string 'self', or null
|
||||
identity: string | 'self' | 'any' // blake3 hash of an identity tangle root msg, or the string 'self', or 'any'
|
||||
identityTips: Array<string> | null // list of blake3 hashes of identity tangle tips, or null
|
||||
tangles: {
|
||||
// for each tangle this msg belongs to, identified by the tangle's root
|
||||
|
@ -67,13 +67,19 @@ interface Msg {
|
|||
}
|
||||
|
||||
type IdentityData =
|
||||
| { action: 'add' add: IdentityAdd }
|
||||
| { action: 'del' del: IdentityDel }
|
||||
| { action: 'add', add: IdentityAdd }
|
||||
| { action: 'del', del: IdentityDel }
|
||||
|
||||
// "add" means this keypair can validly add more keypairs to the identity tangle
|
||||
// "del" means this keypair can validly revoke other keypairs from the identity
|
||||
// "box" means the peer with this keypair should get access to the box keypair
|
||||
type IdentityPower = 'add' | 'del' | 'box'
|
||||
|
||||
type IdentityAdd = {
|
||||
key: Key
|
||||
nonce?: string // nonce required only on the identity tangle's root
|
||||
consent?: string // base58 encoded signature of the string `:identity-add:<ID>` where `<ID>` is the identity's ID, required only on non-root msgs
|
||||
identityPowers?: Array<IdentityPower> // list of powers granted to this key, defaults to []
|
||||
}
|
||||
|
||||
type IdentityDel = {
|
||||
|
@ -84,10 +90,9 @@ type Key =
|
|||
| {
|
||||
purpose: 'sig' // digital signatures
|
||||
algorithm: 'ed25519' // libsodium crypto_sign_detached
|
||||
bytes: string // base58 encoded string for the public key being added
|
||||
bytes: string // base58 encoded string for the public key
|
||||
}
|
||||
| {
|
||||
// WIP!!
|
||||
purpose: 'box' // asymmetric encryption
|
||||
algorithm: 'x25519-xsalsa20-poly1305' // libsodium crypto_box_easy
|
||||
bytes: string // base58 encoded string of the public key
|
||||
|
|
|
@ -34,6 +34,7 @@ test('identity.add()', async (t) => {
|
|||
identity: id,
|
||||
keypair: keypair2,
|
||||
consent,
|
||||
powers: ['box'],
|
||||
})
|
||||
assert.ok(identityRec1, 'identityRec1 exists')
|
||||
const { hash, msg } = identityRec1
|
||||
|
@ -49,6 +50,7 @@ test('identity.add()', async (t) => {
|
|||
bytes: keypair2.public,
|
||||
},
|
||||
consent,
|
||||
powers: ['box'],
|
||||
},
|
||||
},
|
||||
'msg.data.add NEW KEY'
|
||||
|
@ -68,6 +70,86 @@ test('identity.add()', async (t) => {
|
|||
await p(peer.close)()
|
||||
})
|
||||
|
||||
test('keypair with no "add" powers cannot identity.add()', async (t) => {
|
||||
rimraf.sync(DIR)
|
||||
const keypair1 = Keypair.generate('ed25519', 'alice')
|
||||
const keypair2 = Keypair.generate('ed25519', 'bob')
|
||||
const keypair3 = Keypair.generate('ed25519', 'carol')
|
||||
|
||||
const peer1 = SecretStack({ appKey: caps.shse })
|
||||
.use(require('../lib'))
|
||||
.use(require('ssb-box'))
|
||||
.call(null, { keypair: keypair1, path: DIR })
|
||||
|
||||
await peer1.db.loaded()
|
||||
const id = await p(peer1.db.identity.create)({
|
||||
keypair: keypair1,
|
||||
domain: 'account',
|
||||
})
|
||||
const msg1 = peer1.db.get(id)
|
||||
|
||||
const { msg: msg2 } = await p(peer1.db.identity.add)({
|
||||
identity: id,
|
||||
keypair: keypair2,
|
||||
powers: [],
|
||||
})
|
||||
assert.equal(msg2.data.add.key.bytes, keypair2.public)
|
||||
|
||||
assert.equal(peer1.db.identity.has({ identity: id, keypair: keypair2 }), true)
|
||||
|
||||
await p(peer1.close)()
|
||||
rimraf.sync(DIR)
|
||||
|
||||
const peer2 = SecretStack({ appKey: caps.shse })
|
||||
.use(require('../lib'))
|
||||
.use(require('ssb-box'))
|
||||
.call(null, { keypair: keypair2, path: DIR })
|
||||
|
||||
await peer2.db.loaded()
|
||||
await p(peer2.db.add)(msg1, id)
|
||||
await p(peer2.db.add)(msg2, id)
|
||||
|
||||
// Test author-side power validation
|
||||
assert.rejects(
|
||||
p(peer2.db.identity.add)({
|
||||
identity: id,
|
||||
keypair: keypair3,
|
||||
powers: [],
|
||||
}),
|
||||
/signing keypair does not have the "add" power/
|
||||
)
|
||||
|
||||
// Make the author disobey power validation
|
||||
const { msg: msg3 } = await p(peer2.db.identity.add)({
|
||||
identity: id,
|
||||
keypair: keypair3,
|
||||
powers: [],
|
||||
_disobey: true,
|
||||
})
|
||||
|
||||
assert.equal(msg3.data.add.key.bytes, keypair3.public)
|
||||
|
||||
await p(peer2.close)()
|
||||
rimraf.sync(DIR)
|
||||
|
||||
const peer1again = SecretStack({ appKey: caps.shse })
|
||||
.use(require('../lib'))
|
||||
.use(require('ssb-box'))
|
||||
.call(null, { keypair: keypair1, path: DIR })
|
||||
|
||||
await peer1again.db.loaded()
|
||||
await p(peer1again.db.add)(msg1, id) // re-add because lost during rimraf
|
||||
await p(peer1again.db.add)(msg2, id) // re-add because lost during rimraf
|
||||
|
||||
// Test replicator-side power validation
|
||||
assert.rejects(
|
||||
p(peer1again.db.add)(msg3, id),
|
||||
/msg\.pubkey does not have "add" power/
|
||||
)
|
||||
|
||||
await p(peer1again.close)()
|
||||
})
|
||||
|
||||
test('publish with a key in the identity', async (t) => {
|
||||
rimraf.sync(DIR)
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ test('identity.create() with just "domain"', async (t) => {
|
|||
bytes: keypair.public,
|
||||
},
|
||||
nonce: 'MYNONCE',
|
||||
powers: ['add', 'del', 'box'],
|
||||
},
|
||||
},
|
||||
'msg.data.add'
|
||||
|
|
|
@ -21,12 +21,13 @@ test('MsgV3.createIdentity()', (t) => {
|
|||
bytes: keypair.public,
|
||||
},
|
||||
nonce: 'MYNONCE',
|
||||
powers: ['add', 'del', 'box'],
|
||||
},
|
||||
},
|
||||
'data'
|
||||
)
|
||||
assert.equal(identityMsg0.metadata.dataHash, 'C9XZXiZV4kxD6MtVqNkuBw', 'hash')
|
||||
assert.equal(identityMsg0.metadata.dataSize, 143, 'size')
|
||||
assert.equal(identityMsg0.metadata.dataHash, 'R5az9nC1CB3Afd5Q57HYRQ', 'hash')
|
||||
assert.equal(identityMsg0.metadata.dataSize, 172, 'size')
|
||||
assert.equal(identityMsg0.metadata.identity, 'self', 'identity')
|
||||
assert.equal(identityMsg0.metadata.identityTips, null, 'identityTips')
|
||||
assert.deepEqual(identityMsg0.metadata.tangles, {}, 'tangles')
|
||||
|
@ -35,7 +36,7 @@ test('MsgV3.createIdentity()', (t) => {
|
|||
assert.equal(identityMsg0.pubkey, keypair.public, 'pubkey')
|
||||
|
||||
identity = MsgV3.getMsgHash(identityMsg0)
|
||||
assert.equal(identity, 'NwZbYERYSrShDwcKrrLVYe', 'identity ID')
|
||||
assert.equal(identity, 'GZJ1T864pFVHKJ2mRS2c5q', 'identity ID')
|
||||
})
|
||||
|
||||
let rootMsg = null
|
||||
|
@ -57,7 +58,7 @@ test('MsgV3.createRoot()', (t) => {
|
|||
assert.equal(rootMsg.pubkey, keypair.public, 'pubkey')
|
||||
|
||||
rootHash = MsgV3.getMsgHash(rootMsg)
|
||||
assert.equal(rootHash, '2yqmqJLffAeomD93izwjx9', 'root hash')
|
||||
assert.equal(rootHash, '4VfVj9DQArX5Vk6PVz5s5J', 'root hash')
|
||||
})
|
||||
|
||||
test('MsgV3.create()', (t) => {
|
||||
|
@ -125,11 +126,11 @@ test('MsgV3.create()', (t) => {
|
|||
)
|
||||
assert.equal(
|
||||
msg1.sig,
|
||||
'2GsXePCkaJk1emgjRmwTrn9qqA5GozG8BrDa9je4SeCNJX8KVYr45MyZGmfkJsGBoMocZCMhP4jiFgdL1PqURhx6',
|
||||
'23CPZzKBAeRa6gb2ijwUJAd4VrYmokLSbQTmWEFMCiSogjViwqvms6ShyPq1UCzNWKAggmmJP4qETnVrY4iEMQ5J',
|
||||
'sig'
|
||||
)
|
||||
|
||||
const msgHash1 = 'BTybPnRjVjVZMdWBr52kfz'
|
||||
const msgHash1 = 'kF6XHyi1LtJdttRDp54VM'
|
||||
|
||||
assert.equal(
|
||||
MsgV3.getMsgId(msg1),
|
||||
|
@ -201,13 +202,13 @@ test('MsgV3.create()', (t) => {
|
|||
)
|
||||
assert.equal(
|
||||
msg2.sig,
|
||||
'EA8PUp44ZBdgsXa4DRioejc4W5NMapoA9oUbgJwQSsvKDj9nh3oAHn7ScnZo2Hfw67JzHeZXeXVwgLCWzsKCFYw',
|
||||
'tpMaMqV7t4hhYtLPZu7nFmUZej3pXVAYWf3pwXChThsQ8qT9Zxxym2TDDTUrT9VF7CNXRnLNoLMgYuZKAQrZ5bR',
|
||||
'sig'
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
MsgV3.getMsgId(msg2),
|
||||
`ppppp:message/v3/${identity}/post/3bwFna3PvkSNxssPd9QDYe`,
|
||||
`ppppp:message/v3/${identity}/post/7W2nJCdpMeco7D8BYvRq7A`,
|
||||
'getMsgId'
|
||||
)
|
||||
})
|
||||
|
|
|
@ -43,7 +43,7 @@ test('validate identity tangle', (t) => {
|
|||
const keypair2 = Keypair.generate('ed25519', 'bob')
|
||||
|
||||
const identityMsg1 = MsgV3.create({
|
||||
identity: null,
|
||||
identity: 'self',
|
||||
identityTips: null,
|
||||
domain: 'identity',
|
||||
data: { add: keypair2.public },
|
||||
|
|
Loading…
Reference in New Issue