mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-21 08:48:14 +00:00
refactor: aliases have descriptions (#2059)
This commit refactors the ledger Identity type so that aliases are `{address, description}` objects rather than raw addresses. We add a description to the alias for roughly the same reason that we added a description to Graph Nodes in #1191: for UI purposes it's really helpful to be able to render a human-readable description, and trying to keep the descriptions and addresses synchronized in an external data structure is a big pain. In particular, this will enable the ledger admin UI to display already-linked aliases in a human-readable fashion. Since Aliases are usually generated by referencing an existing node in the graph, creating a description for aliases should not be difficult, as we can just use the Graph node's description. Test plan: Unit tests pass and are updated; verify the frontend still works properly.
This commit is contained in:
parent
5fb970fdf1
commit
66a99cbf72
@ -77,10 +77,11 @@ export async function loadLedger(legacyDir: string): Promise<Ledger> {
|
||||
identities.forEach(({username, aliases}) => {
|
||||
username = username.replace("_", "-");
|
||||
const userId = ledger.createIdentity("USER", username);
|
||||
const addresses = aliasParser.parseOrThrow(aliases);
|
||||
addresses.forEach((a) => {
|
||||
ledger.addAlias(userId, a);
|
||||
});
|
||||
const fullAliases = aliases.map((a) => ({
|
||||
address: resolveAlias(a, discourseServer.serverUrl),
|
||||
description: a,
|
||||
}));
|
||||
fullAliases.forEach((a) => ledger.addAlias(userId, a));
|
||||
});
|
||||
|
||||
return ledger;
|
||||
|
@ -1,13 +1,8 @@
|
||||
[
|
||||
{"action":{"identity":{"address":"N\u0000sourcecred\u0000core\u0000IDENTITY\u0000USER\u0000YX6zzoNxYwGgLduJqWVoAg\u0000","aliases":[],"id":"YX6zzoNxYwGgLduJqWVoAg","name":"dandelion","subtype":"USER"},"type":"CREATE_IDENTITY"},"ledgerTimestamp":1595960623457,"uuid":"Z2Yido2VZy7kZNFNQyOrGw","version":"1"},
|
||||
{"action":{"alias":"N\u0000sourcecred\u0000github\u0000USERLIKE\u0000USER\u0000decentralion\u0000","identityId":"YX6zzoNxYwGgLduJqWVoAg","type":"ADD_ALIAS"},"ledgerTimestamp":1595960629051,"uuid":"fJ4p2ql0U9lA8agf98a2WA","version":"1"},
|
||||
{"action":{"alias":"N\u0000sourcecred\u0000discourse\u0000user\u0000https://sourcecred-test.discourse.group\u0000dl-proto\u0000","identityId":"YX6zzoNxYwGgLduJqWVoAg","type":"ADD_ALIAS"},"ledgerTimestamp":1595960631279,"uuid":"hkO0hJe1kBXiW2CkUdqtgA","version":"1"},
|
||||
{"action":{"alias":"N\u0000sourcecred\u0000discourse\u0000user\u0000https://sourcecred-test.discourse.group\u0000d11\u0000","identityId":"YX6zzoNxYwGgLduJqWVoAg","type":"ADD_ALIAS"},"ledgerTimestamp":1595960632917,"uuid":"6n3FlMcRaiapMhAPZBkMzg","version":"1"},
|
||||
{"action":{"identityId":"YX6zzoNxYwGgLduJqWVoAg","type":"TOGGLE_ACTIVATION"},"ledgerTimestamp":1595960669346,"uuid":"JSnR9q3hQmtrxCYW88q3Jw","version":"1"},
|
||||
{"action":{"identity":{"address":"N\u0000sourcecred\u0000core\u0000IDENTITY\u0000USER\u0000mbzRr0D3A2wcD1E7p0H2rQ\u0000","aliases":[],"id":"mbzRr0D3A2wcD1E7p0H2rQ","name":"william","subtype":"USER"},"type":"CREATE_IDENTITY"},"ledgerTimestamp":1595960673321,"uuid":"pdZq9EQYh0HpR4faupeXww","version":"1"},
|
||||
{"action":{"identityId":"mbzRr0D3A2wcD1E7p0H2rQ","type":"TOGGLE_ACTIVATION"},"ledgerTimestamp":1595960684984,"uuid":"lXJfGAKpX2C7eV7WQ13I8A","version":"1"},
|
||||
{"action":{"alias":"N\u0000sourcecred\u0000github\u0000USERLIKE\u0000USER\u0000wchargin\u0000","identityId":"mbzRr0D3A2wcD1E7p0H2rQ","type":"ADD_ALIAS"},"ledgerTimestamp":1595960689227,"uuid":"MrARMedzEewCPhMI4a8tvQ","version":"1"},
|
||||
{"action":{"identity":{"address":"N\u0000sourcecred\u0000core\u0000IDENTITY\u0000USER\u0000ytxt20G2QLd8Z135K0cyUA\u0000","aliases":[],"id":"ytxt20G2QLd8Z135K0cyUA","name":"beanow","subtype":"USER"},"type":"CREATE_IDENTITY"},"ledgerTimestamp":1595960693126,"uuid":"jEn2LpZRZ7he3wYCJ2DIBg","version":"1"},
|
||||
{"action":{"distribution":{"allocations":[{"id":"UJowHzU5CNZ3UAOb3DLoWw","policy":{"budget":"1000000000000000000000","policyType":"BALANCED"},"receipts":[{"amount":"955230194734145601536","id":"YX6zzoNxYwGgLduJqWVoAg"},{"amount":"44769805265854398464","id":"mbzRr0D3A2wcD1E7p0H2rQ"}]}],"credTimestamp":1573344000000,"id":"8CoHqZMDRQ7L1Up6yVqjxQ"},"type":"DISTRIBUTE_GRAIN"},"ledgerTimestamp":1595960739869,"uuid":"R41nebRy7QkSwdWCZ1VW3w","version":"1"},
|
||||
{"action":{"alias":"N\u0000sourcecred\u0000discourse\u0000user\u0000https://sourcecred-test.discourse.group\u0000beanow.sc-test\u0000","identityId":"ytxt20G2QLd8Z135K0cyUA","type":"ADD_ALIAS"},"ledgerTimestamp":1595960759765,"uuid":"bOJeGS9PTpJd91I96mSMQQ","version":"1"}
|
||||
]
|
||||
{"action":{"distribution":{"allocations":[{"id":"UJowHzU5CNZ3UAOb3DLoWw","policy":{"budget":"1000000000000000000000","policyType":"BALANCED"},"receipts":[{"amount":"955230194734145601536","id":"YX6zzoNxYwGgLduJqWVoAg"},{"amount":"44769805265854398464","id":"mbzRr0D3A2wcD1E7p0H2rQ"}]}],"credTimestamp":1573344000000,"id":"8CoHqZMDRQ7L1Up6yVqjxQ"},"type":"DISTRIBUTE_GRAIN"},"ledgerTimestamp":1595960739869,"uuid":"R41nebRy7QkSwdWCZ1VW3w","version":"1"}
|
||||
]
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,7 +4,7 @@ exports[`ledger/ledger state reconstruction serialized ledger snapshots as expec
|
||||
"[
|
||||
{\\"action\\":{\\"identity\\":{\\"address\\":\\"N\\\\u0000sourcecred\\\\u0000core\\\\u0000IDENTITY\\\\u0000USER\\\\u0000YVZhbGlkVXVpZEF0TGFzdA\\\\u0000\\",\\"aliases\\":[],\\"id\\":\\"YVZhbGlkVXVpZEF0TGFzdA\\",\\"name\\":\\"steven\\",\\"subtype\\":\\"USER\\"},\\"type\\":\\"CREATE_IDENTITY\\"},\\"ledgerTimestamp\\":0,\\"uuid\\":\\"000000000000000000000A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"identity\\":{\\"address\\":\\"N\\\\u0000sourcecred\\\\u0000core\\\\u0000IDENTITY\\\\u0000ORGANIZATION\\\\u0000URgLrCxgvjHxtGJ9PgmckQ\\\\u0000\\",\\"aliases\\":[],\\"id\\":\\"URgLrCxgvjHxtGJ9PgmckQ\\",\\"name\\":\\"crystal-gems\\",\\"subtype\\":\\"ORGANIZATION\\"},\\"type\\":\\"CREATE_IDENTITY\\"},\\"ledgerTimestamp\\":1,\\"uuid\\":\\"000000000000000000001A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"alias\\":\\"N\\\\u0000a1\\\\u0000\\",\\"identityId\\":\\"YVZhbGlkVXVpZEF0TGFzdA\\",\\"type\\":\\"ADD_ALIAS\\"},\\"ledgerTimestamp\\":3,\\"uuid\\":\\"000000000000000000002A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"alias\\":{\\"address\\":\\"N\\\\u0000alias\\\\u0000\\",\\"description\\":\\"alias\\"},\\"identityId\\":\\"YVZhbGlkVXVpZEF0TGFzdA\\",\\"type\\":\\"ADD_ALIAS\\"},\\"ledgerTimestamp\\":3,\\"uuid\\":\\"000000000000000000002A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"identityId\\":\\"YVZhbGlkVXVpZEF0TGFzdA\\",\\"type\\":\\"TOGGLE_ACTIVATION\\"},\\"ledgerTimestamp\\":4,\\"uuid\\":\\"000000000000000000003A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"identityId\\":\\"URgLrCxgvjHxtGJ9PgmckQ\\",\\"type\\":\\"TOGGLE_ACTIVATION\\"},\\"ledgerTimestamp\\":4,\\"uuid\\":\\"000000000000000000004A\\",\\"version\\":\\"1\\"},
|
||||
{\\"action\\":{\\"distribution\\":{\\"allocations\\":[{\\"id\\":\\"yYNur0NEEkh7fMaUn6n9QQ\\",\\"policy\\":{\\"budget\\":\\"100\\",\\"policyType\\":\\"IMMEDIATE\\"},\\"receipts\\":[{\\"amount\\":\\"50\\",\\"id\\":\\"YVZhbGlkVXVpZEF0TGFzdA\\"},{\\"amount\\":\\"50\\",\\"id\\":\\"URgLrCxgvjHxtGJ9PgmckQ\\"}]}],\\"credTimestamp\\":1,\\"id\\":\\"f9xPz9YGH0PuBpPAg2824Q\\"},\\"type\\":\\"DISTRIBUTE_GRAIN\\"},\\"ledgerTimestamp\\":4,\\"uuid\\":\\"000000000000000000005A\\",\\"version\\":\\"1\\"},
|
||||
|
@ -20,10 +20,12 @@ describe("ledger/computeDistribution", () => {
|
||||
}));
|
||||
const unclaimedAliases = [
|
||||
{
|
||||
address: NodeAddress.empty,
|
||||
alias: {
|
||||
address: NodeAddress.empty,
|
||||
description: "irrelevant",
|
||||
},
|
||||
cred: [4, 5, 6],
|
||||
totalCred: 15,
|
||||
description: "irrelevant",
|
||||
},
|
||||
];
|
||||
const accountsData = {
|
||||
|
@ -14,6 +14,7 @@ import {Ledger, type Account} from "./ledger";
|
||||
import {CredView} from "../analysis/credView";
|
||||
import {type TimestampMs} from "../util/timestamp";
|
||||
import {NodeAddress, type NodeAddressT} from "../core/graph";
|
||||
import {type Alias} from "./identity";
|
||||
|
||||
export type Cred = $ReadOnlyArray<number>;
|
||||
|
||||
@ -24,10 +25,7 @@ export type CredAccount = {|
|
||||
|};
|
||||
|
||||
export type UnclaimedAlias = {|
|
||||
+address: NodeAddressT,
|
||||
// We include the description for convenience in figuring out who this user is,
|
||||
// rendering in a UI, etc. This is just the description from the Graph.
|
||||
+description: string,
|
||||
+alias: Alias,
|
||||
+totalCred: number,
|
||||
+cred: Cred,
|
||||
|};
|
||||
@ -67,7 +65,7 @@ export function _computeCredAccounts(
|
||||
userlikeInfo: Map<NodeAddressT, {|+cred: Cred, +description: string|}>,
|
||||
intervalEndpoints: $ReadOnlyArray<TimestampMs>
|
||||
): CredAccountData {
|
||||
const aliases: Set<NodeAddressT> = new Set();
|
||||
const aliasAddresses: Set<NodeAddressT> = new Set();
|
||||
const accountAddresses: Set<NodeAddressT> = new Set();
|
||||
|
||||
const accounts = [];
|
||||
@ -76,7 +74,7 @@ export function _computeCredAccounts(
|
||||
for (const account of grainAccounts) {
|
||||
accountAddresses.add(account.identity.address);
|
||||
for (const alias of account.identity.aliases) {
|
||||
aliases.add(alias);
|
||||
aliasAddresses.add(alias.address);
|
||||
}
|
||||
const info = userlikeInfo.get(account.identity.address);
|
||||
if (info == null) {
|
||||
@ -89,24 +87,23 @@ export function _computeCredAccounts(
|
||||
accounts.push(credAccount);
|
||||
}
|
||||
|
||||
for (const [userAddress, info] of userlikeInfo.entries()) {
|
||||
if (accountAddresses.has(userAddress)) {
|
||||
for (const [address, info] of userlikeInfo.entries()) {
|
||||
if (accountAddresses.has(address)) {
|
||||
// This userlike actually has an explicit account
|
||||
continue;
|
||||
}
|
||||
if (aliases.has(userAddress)) {
|
||||
const {cred, description} = info;
|
||||
if (aliasAddresses.has(address)) {
|
||||
throw new Error(
|
||||
`cred sync error: alias ${NodeAddress.toString(
|
||||
userAddress
|
||||
)} included in Cred scores`
|
||||
address
|
||||
)} (aka ${description}) included in Cred scores`
|
||||
);
|
||||
}
|
||||
const {cred, description} = info;
|
||||
unclaimedAliases.push({
|
||||
address: userAddress,
|
||||
alias: {address, description},
|
||||
cred,
|
||||
totalCred: sum(cred),
|
||||
description,
|
||||
});
|
||||
}
|
||||
return {accounts, unclaimedAliases, intervalEndpoints};
|
||||
|
@ -23,10 +23,12 @@ describe("ledger/credAccounts", () => {
|
||||
const intervalEndpoints = [123, 125, 127];
|
||||
const expectedCredAccount = {cred: accountCred, account, totalCred: 3};
|
||||
const expectedUnclaimedAccount = {
|
||||
alias: {
|
||||
address: userAddress,
|
||||
description: "Little lost user",
|
||||
},
|
||||
cred: userCred,
|
||||
totalCred: 2,
|
||||
address: userAddress,
|
||||
description: "Little lost user",
|
||||
};
|
||||
const expectedData = {
|
||||
accounts: [expectedCredAccount],
|
||||
@ -40,8 +42,8 @@ describe("ledger/credAccounts", () => {
|
||||
it("errors if an alias address is in the cred scores", () => {
|
||||
const ledger = new Ledger();
|
||||
const id = ledger.createIdentity("USER", "sourcecred");
|
||||
const userAddress = NodeAddress.empty;
|
||||
ledger.addAlias(id, userAddress);
|
||||
const alias = {address: NodeAddress.empty, description: "user"};
|
||||
ledger.addAlias(id, alias);
|
||||
|
||||
const account = ledger.accounts()[0];
|
||||
const accountCred = [0, 1, 2];
|
||||
@ -51,7 +53,7 @@ describe("ledger/credAccounts", () => {
|
||||
account.identity.address,
|
||||
{cred: accountCred, description: "irrelevant"},
|
||||
],
|
||||
[userAddress, {cred: userCred, description: "irrelevant"}],
|
||||
[alias.address, {cred: userCred, description: "irrelevant"}],
|
||||
]);
|
||||
const intervalEndpoints = [123, 125, 127];
|
||||
|
||||
@ -59,8 +61,8 @@ describe("ledger/credAccounts", () => {
|
||||
_computeCredAccounts([account], info, intervalEndpoints);
|
||||
expect(thunk).toThrowError(
|
||||
`cred sync error: alias ${NodeAddress.toString(
|
||||
userAddress
|
||||
)} included in Cred scores`
|
||||
NodeAddress.empty
|
||||
)} (aka irrelevant) included in Cred scores`
|
||||
);
|
||||
});
|
||||
it("errors if an account doesn't have cred info", () => {
|
||||
|
@ -59,7 +59,18 @@ export type Identity = {|
|
||||
// Every other node in the graph that this identity corresponds to.
|
||||
// Does not include the identity's "own" address, i.e. the result
|
||||
// of calling (identityAddress(identity.id)).
|
||||
+aliases: $ReadOnlyArray<NodeAddressT>,
|
||||
+aliases: $ReadOnlyArray<Alias>,
|
||||
|};
|
||||
|
||||
/**
|
||||
* An Alias is basically another graph Node which resolves to this identity. We
|
||||
* ignore the timestamp because it's generally not significant for users; we
|
||||
* keep the address out of obvious necessity, and we keep the description so we
|
||||
* can describe this alias in UIs (e.g. the ledger admin panel).
|
||||
*/
|
||||
export type Alias = {|
|
||||
+description: string,
|
||||
+address: NodeAddressT,
|
||||
|};
|
||||
|
||||
export function newIdentity(subtype: IdentitySubtype, name: string): Identity {
|
||||
@ -109,7 +120,10 @@ export function graphNode({name, address}: Identity): GraphNode {
|
||||
export function contractions(
|
||||
identities: $ReadOnlyArray<Identity>
|
||||
): $ReadOnlyArray<NodeContraction> {
|
||||
return identities.map((i) => ({replacement: graphNode(i), old: i.aliases}));
|
||||
return identities.map((i) => ({
|
||||
replacement: graphNode(i),
|
||||
old: i.aliases.map((a) => a.address),
|
||||
}));
|
||||
}
|
||||
|
||||
export const identityNameParser: C.Parser<IdentityName> = C.fmap(
|
||||
@ -124,12 +138,17 @@ export const identitySubtypeParser: C.Parser<IdentitySubtype> = C.exactly([
|
||||
"PROJECT",
|
||||
]);
|
||||
|
||||
export const aliasParser: C.Parser<Alias> = C.object({
|
||||
address: NodeAddress.parser,
|
||||
description: C.string,
|
||||
});
|
||||
|
||||
export const identityParser: C.Parser<Identity> = C.object({
|
||||
id: uuidParser,
|
||||
subtype: identitySubtypeParser,
|
||||
name: identityNameParser,
|
||||
address: NodeAddress.parser,
|
||||
aliases: C.array(NodeAddress.parser),
|
||||
aliases: C.array(aliasParser),
|
||||
});
|
||||
|
||||
const userNodeType: NodeType = {
|
||||
|
@ -17,6 +17,8 @@ import {
|
||||
newIdentity,
|
||||
identityNameParser,
|
||||
identityNameFromString,
|
||||
type Alias,
|
||||
aliasParser,
|
||||
} from "./identity";
|
||||
import {type NodeAddressT, NodeAddress} from "../core/graph";
|
||||
import {type TimestampMs} from "../util/timestamp";
|
||||
@ -74,7 +76,7 @@ export type Account = $ReadOnly<MutableAccount>;
|
||||
export class Ledger {
|
||||
_ledgerEventLog: JsonLog<LedgerEvent>;
|
||||
_identityNameToId: Map<IdentityName, IdentityId>;
|
||||
_aliases: Map<NodeAddressT, IdentityId>;
|
||||
_aliasAddressToIdentity: Map<NodeAddressT, IdentityId>;
|
||||
_accounts: Map<IdentityId, MutableAccount>;
|
||||
_latestTimestamp: TimestampMs = -Infinity;
|
||||
_lastDistributionTimestamp: TimestampMs = -Infinity;
|
||||
@ -82,7 +84,7 @@ export class Ledger {
|
||||
constructor() {
|
||||
this._ledgerEventLog = new JsonLog();
|
||||
this._identityNameToId = new Map();
|
||||
this._aliases = new Map();
|
||||
this._aliasAddressToIdentity = new Map();
|
||||
this._accounts = new Map();
|
||||
}
|
||||
|
||||
@ -141,7 +143,7 @@ export class Ledger {
|
||||
throw new Error(`createIdentity: new identities may not have aliases`);
|
||||
}
|
||||
// istanbul ignore if
|
||||
if (this._aliases.has(identity.address)) {
|
||||
if (this._aliasAddressToIdentity.has(identity.address)) {
|
||||
// This should never happen, as it implies a UUID conflict.
|
||||
throw new Error(
|
||||
`createIdentity: innate address already claimed ${identity.id}`
|
||||
@ -151,7 +153,7 @@ export class Ledger {
|
||||
// Mutations! Method must not fail after this comment.
|
||||
this._identityNameToId.set(identity.name, identity.id);
|
||||
// Reserve this identity's own address
|
||||
this._aliases.set(identity.address, identity.id);
|
||||
this._aliasAddressToIdentity.set(identity.address, identity.id);
|
||||
// Every identity has a corresponding Account.
|
||||
this._accounts.set(identity.id, {
|
||||
balance: G.ZERO,
|
||||
@ -216,7 +218,7 @@ export class Ledger {
|
||||
* Will fail if the identity does not exist.
|
||||
* Will fail if the alias is already claimed by any identity.
|
||||
*/
|
||||
addAlias(identityId: IdentityId, alias: NodeAddressT): Ledger {
|
||||
addAlias(identityId: IdentityId, alias: Alias): Ledger {
|
||||
this._createAndProcessEvent({
|
||||
type: "ADD_ALIAS",
|
||||
identityId,
|
||||
@ -231,22 +233,22 @@ export class Ledger {
|
||||
const account = this._mutableAccount(identityId);
|
||||
const existingIdentity = account.identity;
|
||||
const existingAliases = existingIdentity.aliases;
|
||||
if (existingAliases.indexOf(alias) !== -1) {
|
||||
if (existingAliases.map((a) => a.address).indexOf(alias.address) !== -1) {
|
||||
throw new Error(
|
||||
`addAlias: identity already has alias: ${
|
||||
existingIdentity.name
|
||||
}, ${NodeAddress.toString(alias)}`
|
||||
}, ${NodeAddress.toString(alias.address)}`
|
||||
);
|
||||
}
|
||||
if (this._aliases.has(alias)) {
|
||||
if (this._aliasAddressToIdentity.has(alias.address)) {
|
||||
// Some other identity has this alias; fail.
|
||||
throw new Error(
|
||||
`addAlias: alias ${NodeAddress.toString(alias)} already bound`
|
||||
`addAlias: alias ${NodeAddress.toString(alias.address)} already bound`
|
||||
);
|
||||
}
|
||||
|
||||
// Mutations below; method must not fail after this line.
|
||||
this._aliases.set(alias, identityId);
|
||||
this._aliasAddressToIdentity.set(alias.address, identityId);
|
||||
const updatedAliases = existingIdentity.aliases.slice();
|
||||
updatedAliases.push(alias);
|
||||
const updatedIdentity = {
|
||||
@ -552,12 +554,12 @@ const renameIdentityParser: C.Parser<RenameIdentity> = C.object({
|
||||
type AddAlias = {|
|
||||
+type: "ADD_ALIAS",
|
||||
+identityId: IdentityId,
|
||||
+alias: NodeAddressT,
|
||||
+alias: Alias,
|
||||
|};
|
||||
const addAliasParser: C.Parser<AddAlias> = C.object({
|
||||
type: C.exactly(["ADD_ALIAS"]),
|
||||
identityId: uuid.parser,
|
||||
alias: NodeAddress.parser,
|
||||
alias: aliasParser,
|
||||
});
|
||||
|
||||
type ToggleActivation = {|
|
||||
|
@ -80,7 +80,10 @@ describe("ledger/ledger", () => {
|
||||
return ledger;
|
||||
}
|
||||
|
||||
const a1 = NodeAddress.fromParts(["a1"]);
|
||||
const alias = {
|
||||
address: NodeAddress.fromParts(["alias"]),
|
||||
description: "alias",
|
||||
};
|
||||
|
||||
describe("identity updates", () => {
|
||||
describe("createIdentity", () => {
|
||||
@ -124,7 +127,10 @@ describe("ledger/ledger", () => {
|
||||
it("throws an error given an identity with aliases", () => {
|
||||
const ledger = new Ledger();
|
||||
let identity = newIdentity("USER", "foo");
|
||||
identity = {...identity, aliases: [NodeAddress.empty]};
|
||||
identity = {
|
||||
...identity,
|
||||
aliases: [{address: NodeAddress.empty, description: "foo"}],
|
||||
};
|
||||
const action = {type: "CREATE_IDENTITY", identity};
|
||||
const thunk = () => ledger._createIdentity(action);
|
||||
expect(thunk).toThrowError("new identities may not have aliases");
|
||||
@ -214,9 +220,9 @@ describe("ledger/ledger", () => {
|
||||
setFakeDate(0);
|
||||
const id = ledger.createIdentity("USER", "foo");
|
||||
setFakeDate(1);
|
||||
ledger.addAlias(id, a1);
|
||||
ledger.addAlias(id, alias);
|
||||
const identity = ledger.account(id).identity;
|
||||
expect(identity.aliases).toEqual([a1]);
|
||||
expect(identity.aliases).toEqual([alias]);
|
||||
expect(ledger.eventLog()).toEqual([
|
||||
{
|
||||
ledgerTimestamp: 0,
|
||||
@ -234,43 +240,60 @@ describe("ledger/ledger", () => {
|
||||
action: {
|
||||
type: "ADD_ALIAS",
|
||||
identityId: id,
|
||||
alias: a1,
|
||||
alias: alias,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
it("adding multiple aliases with the same description is fine", () => {
|
||||
const ledger = new Ledger();
|
||||
const id = ledger.createIdentity("USER", "foo");
|
||||
const a1 = {
|
||||
address: NodeAddress.fromParts(["1"]),
|
||||
description: "alias",
|
||||
};
|
||||
const a2 = {
|
||||
address: NodeAddress.fromParts(["2"]),
|
||||
description: "alias",
|
||||
};
|
||||
ledger.addAlias(id, a1);
|
||||
ledger.addAlias(id, a2);
|
||||
const identity = ledger.account(id).identity;
|
||||
expect(identity.aliases).toEqual([a1, a2]);
|
||||
});
|
||||
it("errors if there's no matching identity", () => {
|
||||
const ledger = new Ledger();
|
||||
failsWithoutMutation(
|
||||
ledger,
|
||||
(l) => l.addAlias(uuid.random(), a1),
|
||||
(l) => l.addAlias(uuid.random(), alias),
|
||||
"no identity matches id"
|
||||
);
|
||||
});
|
||||
it("throws an error if the identity already has that alias", () => {
|
||||
const ledger = new Ledger();
|
||||
const id = ledger.createIdentity("USER", "foo");
|
||||
ledger.addAlias(id, a1);
|
||||
const thunk = () => ledger.addAlias(id, a1);
|
||||
ledger.addAlias(id, alias);
|
||||
const thunk = () => ledger.addAlias(id, alias);
|
||||
failsWithoutMutation(ledger, thunk, "identity already has alias");
|
||||
});
|
||||
it("errors if the address is another identity's alias", () => {
|
||||
const ledger = new Ledger();
|
||||
const id1 = ledger.createIdentity("USER", "foo");
|
||||
const id2 = ledger.createIdentity("USER", "bar");
|
||||
ledger.addAlias(id1, a1);
|
||||
const thunk = () => ledger.addAlias(id2, a1);
|
||||
ledger.addAlias(id1, alias);
|
||||
const thunk = () => ledger.addAlias(id2, alias);
|
||||
failsWithoutMutation(
|
||||
ledger,
|
||||
thunk,
|
||||
`addAlias: alias ${NodeAddress.toString(a1)} already bound`
|
||||
`addAlias: alias ${NodeAddress.toString(alias.address)} already bound`
|
||||
);
|
||||
});
|
||||
it("errors if the address is the identity's innate address", () => {
|
||||
const ledger = new Ledger();
|
||||
const id = ledger.createIdentity("USER", "foo");
|
||||
const identity = ledger.account(id).identity;
|
||||
const thunk = () => ledger.addAlias(id, identity.address);
|
||||
const thunk = () =>
|
||||
ledger.addAlias(id, {address: identity.address, description: ""});
|
||||
failsWithoutMutation(
|
||||
ledger,
|
||||
thunk,
|
||||
@ -281,7 +304,8 @@ describe("ledger/ledger", () => {
|
||||
});
|
||||
it("errors if the address is another identity's innate address", () => {
|
||||
const ledger = ledgerWithIdentities();
|
||||
const thunk = () => ledger.addAlias(id2, identity1().address);
|
||||
const thunk = () =>
|
||||
ledger.addAlias(id2, {address: identity1().address, description: ""});
|
||||
failsWithoutMutation(
|
||||
ledger,
|
||||
thunk,
|
||||
@ -744,7 +768,7 @@ describe("ledger/ledger", () => {
|
||||
function richLedger(): Ledger {
|
||||
const ledger = ledgerWithIdentities();
|
||||
setFakeDate(3);
|
||||
ledger.addAlias(id1, a1);
|
||||
ledger.addAlias(id1, alias);
|
||||
|
||||
const distributionId = uuid.fromString("f9xPz9YGH0PuBpPAg2824Q");
|
||||
const allocationId = uuid.fromString("yYNur0NEEkh7fMaUn6n9QQ");
|
||||
|
@ -3,7 +3,7 @@
|
||||
import React, {useState, useMemo} from "react";
|
||||
import {useCombobox, useMultipleSelection} from "downshift";
|
||||
import {Ledger} from "../../ledger/ledger";
|
||||
import {type Identity} from "../../ledger/identity";
|
||||
import {type Identity, type Alias} from "../../ledger/identity";
|
||||
import {CredView} from "../../analysis/credView";
|
||||
import {type NodeAddressT} from "../../core/graph";
|
||||
import Markdown from "react-markdown";
|
||||
@ -16,11 +16,6 @@ type Props = {|
|
||||
+setCurrentIdentity: (Identity) => void,
|
||||
|};
|
||||
|
||||
type Alias = {|
|
||||
+address: NodeAddressT,
|
||||
+description: string,
|
||||
|};
|
||||
|
||||
export function AliasSelector({
|
||||
currentIdentity,
|
||||
ledger,
|
||||
@ -50,7 +45,7 @@ export function AliasSelector({
|
||||
const claimedAddresses: Set<NodeAddressT> = new Set();
|
||||
for (const {identity} of ledger.accounts()) {
|
||||
claimedAddresses.add(identity.address);
|
||||
for (const address of identity.aliases) {
|
||||
for (const {address} of identity.aliases) {
|
||||
claimedAddresses.add(address);
|
||||
}
|
||||
}
|
||||
@ -103,9 +98,7 @@ export function AliasSelector({
|
||||
case useCombobox.stateChangeTypes.ItemClick:
|
||||
case useCombobox.stateChangeTypes.InputBlur:
|
||||
if (selectedItem && currentIdentity) {
|
||||
setLedger(
|
||||
ledger.addAlias(currentIdentity.id, selectedItem.address)
|
||||
);
|
||||
setLedger(ledger.addAlias(currentIdentity.id, selectedItem));
|
||||
setCurrentIdentity(ledger.account(currentIdentity.id).identity);
|
||||
setInputValue("");
|
||||
addSelectedItem(selectedItem);
|
||||
|
Loading…
x
Reference in New Issue
Block a user