IPFS compatible text input for alliance CID (#7928)

* IPFS compatible text input for alliance CID

* Adjust detect
This commit is contained in:
Jaco 2022-07-28 16:18:40 +02:00 committed by GitHub
parent 8a47543e03
commit 4e90ec9663
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 138 additions and 59 deletions

View File

@ -13,7 +13,6 @@
"type": "module",
"version": "0.118.2-22-x",
"dependencies": {
"@babel/runtime": "^7.18.9",
"multiformats": "^9.7.1"
"@babel/runtime": "^7.18.9"
}
}

View File

@ -4,7 +4,7 @@
import { Metadata, TypeRegistry } from '@polkadot/types';
import substrate from '@polkadot/types-support/metadata/static-substrate';
import { createIpfsHash, createPalletCid } from './util';
import { createCid, createPalletCid } from './util';
const registry = new TypeRegistry();
const metadata = new Metadata(registry, substrate);
@ -29,17 +29,17 @@ describe('util', (): void => {
describe('createIpfsHash', (): void => {
it('encodes a pallet CID into an ipfs hash', (): void => {
const cid = registry.createType('PalletAllianceCid', {
codec: 0x70,
hash_: {
code: 0x12,
digest: '0x5360ecbd380c10f43bc0a6aba27556580011a5e833592df8dcf330c94759e862'
},
version: 'V0'
});
expect(
createIpfsHash(
registry.createType('PalletAllianceCid', {
codec: 0x70,
hash_: {
code: 0x12,
digest: '0x5360ecbd380c10f43bc0a6aba27556580011a5e833592df8dcf330c94759e862'
},
version: 'V0'
})
)
createCid(cid).ipfs
).toEqual('QmTx8GZrQGwQ1ZLquZ4yPqSukDdkvKwpYCULsfLo6G47TX');
});
});

View File

@ -5,58 +5,18 @@ import type { PalletAllianceCid } from '@polkadot/types/lookup';
import type { Registry } from '@polkadot/types/types';
import type { Cid } from './types';
import { CID, digest, varint } from 'multiformats';
import { u8aToHex } from '@polkadot/util';
export function createIpfsHash (cid: PalletAllianceCid): string | null {
try {
const { codec, hash_: { code, digest: _bytes }, version } = cid;
// Since we use parse, encode into a fully-specified bytes to
// pass - <varint code> + <varint length> + bytes
const bytes = _bytes.toU8a(true);
const codeLen = varint.encodingLength(code.toNumber());
const sizeLen = varint.encodingLength(bytes.length);
const encoded = new Uint8Array(codeLen + sizeLen + bytes.length);
varint.encodeTo(code.toNumber(), encoded, 0);
varint.encodeTo(bytes.length, encoded, codeLen);
encoded.set(bytes, codeLen + sizeLen);
return CID
.create(version.index as 0, codec.toNumber(), digest.decode(encoded))
.toString();
} catch (error) {
console.error(`createIpfs: ${(error as Error).message}::`, cid.toHuman());
return null;
}
}
import { fromIpfsCid, toIpfsCid } from '@polkadot/react-params/util';
export function createPalletCid (registry: Registry, cid: string): PalletAllianceCid | null {
try {
const { code: codec, multihash: { code, digest }, version } = CID.parse(cid);
const expanded = fromIpfsCid(cid);
return registry.createType('PalletAllianceCid', {
codec,
hash: {
code,
digest: u8aToHex(digest)
},
version
});
} catch (error) {
console.error(`encodeCid: ${(error as Error).message}::`, cid);
return null;
}
return expanded && registry.createType('PalletAllianceCid', expanded);
}
export function createCid (cid: PalletAllianceCid): Cid {
return {
cid,
ipfs: createIpfsHash(cid),
ipfs: toIpfsCid(cid),
key: cid.toHex()
};
}

View File

@ -14,6 +14,7 @@
"version": "0.118.2-22-x",
"dependencies": {
"@babel/runtime": "^7.18.9",
"@polkadot/react-components": "^0.118.2-22-x"
"@polkadot/react-components": "^0.118.2-22-x",
"multiformats": "^9.7.1"
}
}

View File

@ -0,0 +1,54 @@
// Copyright 2017-2022 @polkadot/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types';
import React, { useCallback, useState } from 'react';
import { Input } from '@polkadot/react-components';
import { isCodec } from '@polkadot/util';
import { fromIpfsCid } from '../util';
import Bare from './Bare';
import Struct from './Struct';
function Cid (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue, isDisabled, isError, label, onChange, withLabel } = props;
const [isValid, setIsValid] = useState(false);
const [isStruct] = useState<boolean>(() => isDisabled || !defaultValue || isCodec(defaultValue.value));
const _onChange = useCallback(
(_value: string): void => {
const value = fromIpfsCid(_value);
const isValid = !!value;
onChange && onChange({
isValid,
value
});
setIsValid(isValid);
},
[onChange]
);
if (isStruct) {
return <Struct {...props} />;
}
return (
<Bare className={className}>
<Input
className='full'
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
placeholder='IPFS compatible CID'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Cid);

View File

@ -14,6 +14,7 @@ import Balance from './Balance';
import Bool from './Bool';
import Bytes from './Bytes';
import Call from './Call';
import Cid from './Cid';
import Code from './Code';
import DispatchError from './DispatchError';
import DispatchResult from './DispatchResult';
@ -53,6 +54,7 @@ const componentDef: TypeToComponent[] = [
{ c: Bool, t: ['bool'] },
{ c: Bytes, t: ['Bytes', 'Vec<u8>'] },
{ c: Call, t: ['Call', 'Proposal'] },
{ c: Cid, t: ['PalletAllianceCid'] },
{ c: Code, t: ['Code'] },
{ c: DispatchError, t: DISPATCH_ERROR },
{ c: DispatchResult, t: ['DispatchResult', 'Result<Null, SpRuntimeDispatchError>'] },

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { Registry, TypeDef } from '@polkadot/types/types';
import type { HexString } from '@polkadot/util/types';
// FIXME Ideally, we want these as Base from api-codec - as a stop-gap, any this until we have
// params returning types extending Base (i.e. anything from api-codec)
@ -56,3 +57,12 @@ export interface ParamDef {
name?: string;
type: TypeDef;
}
export interface ExpandedCid {
codec: number;
hash: {
code: number;
digest: HexString;
};
version: 0 | 1;
}

View File

@ -0,0 +1,53 @@
// Copyright 2017-2022 @polkadot/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@polkadot/types/lookup';
import type { ExpandedCid } from './types';
import { CID, digest, varint } from 'multiformats';
import { u8aToHex } from '@polkadot/util';
export function fromIpfsCid (cid: string): ExpandedCid | null {
try {
const { code: codec, multihash: { code, digest }, version } = CID.parse(cid);
return {
codec,
hash: {
code,
digest: u8aToHex(digest)
},
version
};
} catch (error) {
console.error(`fromIpfsCid: ${(error as Error).message}::`, cid);
return null;
}
}
export function toIpfsCid (cid: PalletAllianceCid): string | null {
try {
const { codec, hash_: { code, digest: _bytes }, version } = cid;
// Since we use parse, encode into a fully-specified bytes to
// pass - <varint code> + <varint length> + bytes
const bytes = _bytes.toU8a(true);
const codeLen = varint.encodingLength(code.toNumber());
const sizeLen = varint.encodingLength(bytes.length);
const encoded = new Uint8Array(codeLen + sizeLen + bytes.length);
varint.encodeTo(code.toNumber(), encoded, 0);
varint.encodeTo(bytes.length, encoded, codeLen);
encoded.set(bytes, codeLen + sizeLen);
return CID
.create(version.index as 0, codec.toNumber(), digest.decode(encoded))
.toString();
} catch (error) {
console.error(`toIpfsCid: ${(error as Error).message}::`, cid.toHuman());
return null;
}
}

View File

@ -2766,7 +2766,6 @@ __metadata:
resolution: "@polkadot/app-alliance@workspace:packages/page-alliance"
dependencies:
"@babel/runtime": ^7.18.9
multiformats: ^9.7.1
languageName: unknown
linkType: soft
@ -3467,6 +3466,7 @@ __metadata:
dependencies:
"@babel/runtime": ^7.18.9
"@polkadot/react-components": ^0.118.2-22-x
multiformats: ^9.7.1
languageName: unknown
linkType: soft