Skip to content

Commit

Permalink
Merge pull request #1704 from blockscout/hotfix/v1.26.2
Browse files Browse the repository at this point in the history
Hotfix `v1.26.2`
  • Loading branch information
tom2drum authored Mar 15, 2024
2 parents 9278adb + 12f0781 commit 8c1be25
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 26 deletions.
2 changes: 1 addition & 1 deletion deploy/values/review/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ frontend:
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/goerli.svg
NEXT_PUBLIC_API_HOST: eth-goerli.blockscout.com
NEXT_PUBLIC_API_HOST: eth-sepolia.blockscout.com
NEXT_PUBLIC_STATS_API_HOST: https://stats-goerli.k8s-dev.blockscout.com/
NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/
NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com
Expand Down
5 changes: 4 additions & 1 deletion lib/blob/guessDataType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import filetype from 'magic-bytes.js';

import hexToBytes from 'lib/hexToBytes';

import removeNonSignificantZeroBytes from './removeNonSignificantZeroBytes';

export default function guessDataType(data: string) {
const bytes = new Uint8Array(hexToBytes(data));
const filteredBytes = removeNonSignificantZeroBytes(bytes);

return filetype(bytes)[0];
return filetype(filteredBytes)[0];
}
20 changes: 20 additions & 0 deletions lib/blob/removeNonSignificantZeroBytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export default function removeNonSignificantZeroBytes(bytes: Uint8Array) {
return shouldRemoveBytes(bytes) ? bytes.filter((item, index) => index % 32) : bytes;
}

// check if every 0, 32, 64, etc byte is 0 in the provided array
function shouldRemoveBytes(bytes: Uint8Array) {
let result = true;

for (let index = 0; index < bytes.length; index += 32) {
const element = bytes[index];
if (element === 0) {
continue;
} else {
result = false;
break;
}
}

return result;
}
10 changes: 10 additions & 0 deletions lib/bytesToBase64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function bytesToBase64(bytes: Uint8Array) {
let binary = '';
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}

const base64String = btoa(binary);

return base64String;
}
10 changes: 2 additions & 8 deletions lib/hexToBase64.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import bytesToBase64 from './bytesToBase64';
import hexToBytes from './hexToBytes';

export default function hexToBase64(hex: string) {
const bytes = new Uint8Array(hexToBytes(hex));

let binary = '';
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}

const base64String = btoa(binary);

return base64String;
return bytesToBase64(bytes);
}
6 changes: 3 additions & 3 deletions ui/address/contract/methodForm/useFormatFieldValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ export default function useFormatFieldValue({ argType, argTypeMatchInt }: Params

if (argTypeMatchInt) {
const formattedString = value.replace(/\s/g, '');
return parseInt(formattedString);
return formattedString;
}

if (argType === 'bool') {
const formattedValue = value.toLowerCase();

switch (formattedValue) {
case 'true': {
return true;
return 'true';
}

case 'false':{
return false;
return 'false';
}

default:
Expand Down
14 changes: 7 additions & 7 deletions ui/address/contract/methodForm/useValidateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
return argType.match(BYTES_REGEXP);
}, [ argType ]);

// some values are formatted before they are sent to the validator
// see ./useFormatFieldValue.tsx hook
return React.useCallback((value: string | number | boolean | undefined) => {
return React.useCallback((value: string | undefined) => {
if (value === undefined || value === '') {
return isOptional ? true : 'Field is required';
}

if (argType === 'address') {
if (typeof value !== 'string' || !isAddress(value)) {
if (!isAddress(value)) {
return 'Invalid address format';
}

Expand All @@ -41,11 +39,13 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
}

if (argTypeMatchInt) {
if (typeof value !== 'number' || Object.is(value, NaN)) {
const formattedValue = Number(value);

if (Object.is(formattedValue, NaN)) {
return 'Invalid integer format';
}

if (value > argTypeMatchInt.max || value < argTypeMatchInt.min) {
if (formattedValue > argTypeMatchInt.max || formattedValue < argTypeMatchInt.min) {
const lowerBoundary = argTypeMatchInt.isUnsigned ? '0' : `-1 * 2 ^ ${ Number(argTypeMatchInt.power) - 1 }`;
const upperBoundary = argTypeMatchInt.isUnsigned ? `2 ^ ${ argTypeMatchInt.power } - 1` : `2 ^ ${ Number(argTypeMatchInt.power) - 1 } - 1`;
return `Value should be in range from "${ lowerBoundary }" to "${ upperBoundary }" inclusively`;
Expand All @@ -55,7 +55,7 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
}

if (argType === 'bool') {
if (typeof value !== 'boolean') {
if (value !== 'true' && value !== 'false') {
return 'Invalid boolean format. Allowed values: true, false';
}
}
Expand Down
11 changes: 11 additions & 0 deletions ui/blob/BlobData.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import TestApp from 'playwright/TestApp';

import BlobData from './BlobData';
import imageBlobWithZeroesBytes from './image_with_zeroes.blob';

test.use({ viewport: { width: 500, height: 300 } });

Expand Down Expand Up @@ -40,3 +41,13 @@ test('image', async({ mount }) => {

await expect(component).toHaveScreenshot();
});

test('image blob with zeroes bytes', async({ mount }) => {
const component = await mount(
<TestApp>
<BlobData hash="0x01" data={ imageBlobWithZeroesBytes }/>
</TestApp>,
);

await expect(component).toHaveScreenshot();
});
16 changes: 11 additions & 5 deletions ui/blob/BlobData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Flex, GridItem, Select, Skeleton, Button } from '@chakra-ui/react';
import React from 'react';

import * as blobUtils from 'lib/blob';
import removeNonSignificantZeroBytes from 'lib/blob/removeNonSignificantZeroBytes';
import bytesToBase64 from 'lib/bytesToBase64';
import downloadBlob from 'lib/downloadBlob';
import hexToBase64 from 'lib/hexToBase64';
import hexToBytes from 'lib/hexToBytes';
Expand Down Expand Up @@ -49,7 +51,8 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
switch (format) {
case 'Image': {
const bytes = new Uint8Array(hexToBytes(data));
return new Blob([ bytes ], { type: guessedType?.mime });
const filteredBytes = removeNonSignificantZeroBytes(bytes);
return new Blob([ filteredBytes ], { type: guessedType?.mime });
}
case 'UTF-8': {
return new Blob([ hexToUtf8(data) ], { type: guessedType?.mime ?? 'text/plain' });
Expand All @@ -74,19 +77,22 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
return <RawDataSnippet data="Not an image" showCopy={ false } isLoading={ isLoading }/>;
}

const base64 = hexToBase64(data);
const bytes = new Uint8Array(hexToBytes(data));
const filteredBytes = removeNonSignificantZeroBytes(bytes);
const base64 = bytesToBase64(filteredBytes);

const imgSrc = `data:${ guessedType.mime };base64,${ base64 }`;

return <BlobDataImage src={ imgSrc }/>;
}
case 'UTF-8':
return <RawDataSnippet data={ hexToUtf8(data) } showCopy={ false } isLoading={ isLoading }/>;
return <RawDataSnippet data={ hexToUtf8(data) } showCopy={ false } isLoading={ isLoading } contentProps={{ wordBreak: 'break-word' }}/>;
case 'Base64':
return <RawDataSnippet data={ hexToBase64(data) } showCopy={ false } isLoading={ isLoading }/>;
case 'Raw':
return <RawDataSnippet data={ data } showCopy={ false } isLoading={ isLoading }/>;
default:
return <span>fallback</span>;
return <span/>;
}
})();

Expand Down Expand Up @@ -119,7 +125,7 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
Download
</Button>
</Skeleton>
<CopyToClipboard text={ JSON.stringify(data) } isLoading={ isLoading }/>
<CopyToClipboard text={ data } isLoading={ isLoading }/>
</Flex>
{ content }
</GridItem>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions ui/blob/image_with_zeroes.blob.ts

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion ui/shared/RawDataSnippet.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ChakraProps } from '@chakra-ui/react';
import { Box, Flex, chakra, useColorModeValue, Skeleton } from '@chakra-ui/react';
import React from 'react';

Expand All @@ -12,9 +13,10 @@ interface Props {
textareaMaxHeight?: string;
showCopy?: boolean;
isLoading?: boolean;
contentProps?: ChakraProps;
}

const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textareaMaxHeight, showCopy = true, isLoading }: Props) => {
const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textareaMaxHeight, showCopy = true, isLoading, contentProps }: Props) => {
// see issue in theme/components/Textarea.ts
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b');
return (
Expand All @@ -39,6 +41,7 @@ const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textare
overflowX="hidden"
overflowY="auto"
isLoaded={ !isLoading }
{ ...contentProps }
>
{ data }
</Skeleton>
Expand Down

0 comments on commit 8c1be25

Please sign in to comment.