Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add display of contract disassembly in ContractDetails page (#817) #818

Merged
merged 51 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e7e2402
feat: added EVM bytecode Disassembler util class
quiet-node Nov 24, 2023
1923e84
feat: added BycodeTool component
quiet-node Nov 24, 2023
cd1d891
update: integrated BycodeTools for Disassmbler tool to main contract …
quiet-node Nov 24, 2023
87e366e
feat: added a new bytecode-tools route to host new BytecodeAnalyzerTo…
quiet-node Nov 24, 2023
b7e546a
fix(test): fixed unit tests
quiet-node Nov 25, 2023
e5603f4
test: added new test for BytecodeAnalyzerTools
quiet-node Nov 25, 2023
b7ebb72
feat: added removeCBORMetadata() in bytecode disassembler
quiet-node Nov 28, 2023
1421635
update: reorganized bytecode tool util folder
quiet-node Nov 28, 2023
0e52d59
update: added DisassembledOpcodeOutput interface
quiet-node Nov 29, 2023
3fd291a
feat: added Decompiler util class
quiet-node Nov 29, 2023
8dfef36
feat: integrated Decompiler class and showcase decompiled solidity co…
quiet-node Nov 29, 2023
e46bd01
update: added prism to highlight decompiled solidity code
quiet-node Nov 29, 2023
9584090
update: reworked bycode tools controller logic
quiet-node Nov 29, 2023
c391456
update: updated bytecode tools
quiet-node Dec 4, 2023
dcfb374
fix(style): fixed testBytecodeToolsRoute to compare to the correct value
quiet-node Dec 4, 2023
7b0493c
update: removed param in makeRouteToBytecodeTools()
quiet-node Dec 4, 2023
c76401c
feat: added keyhash to rest calls to decompiler service
quiet-node Dec 4, 2023
f205eef
update: added loading effect to disassmbler and decompiler
quiet-node Dec 5, 2023
a158220
update: removed unused component
quiet-node Dec 5, 2023
1d59b8a
udpate: reorganized disassembler folder
quiet-node Dec 14, 2023
788c871
chore(docs): added a evm version flag to EVM_OPCODES object
quiet-node Dec 14, 2023
3c9af81
update: removed decompiler tool
quiet-node Dec 14, 2023
d559c92
Display 'Disassembled Bytecode' property at all times in ContractByte…
svienot Dec 20, 2023
a78e246
Adjust unit tests.
svienot Dec 20, 2023
48a8c8a
Introduce OpcodeValue to resolve (and link to) contract/account addre…
svienot Dec 20, 2023
0f98ecd
Used computed in DisassembledCodeValue.
svienot Dec 21, 2023
c0670f9
Take care of initialLoading in DisassembledCodeValue.
svienot Dec 21, 2023
cfd77b3
Type computed in DisassembledCodeValue.
svienot Dec 21, 2023
4ebc2a3
Show invalid opcodes with contrast.
svienot Dec 21, 2023
9b9ccfc
fix: fixed operation index
quiet-node Dec 21, 2023
e2f1cba
Add optional display of hexa opcode in disassembled bytecode.
svienot Dec 21, 2023
30add4d
Show invalid opcode in lowercase.
svienot Dec 22, 2023
05364b8
Restore default networks-config.json (remove localnode).
svienot Dec 22, 2023
8ab883d
Make showOpcodeHexa false by default.
svienot Dec 22, 2023
0f370e2
Compiler Version -> Solidity Compiler Version
svienot Dec 22, 2023
e8e6794
Display Recent Contract Calls above Contract Bytecode
svienot Dec 22, 2023
eac6e5b
Adjust unit tests.
svienot Dec 22, 2023
636e7b1
Make left and right content wrap in DashboardCard.
svienot Dec 22, 2023
25f311b
Preserve left/right content proportions in DashboardCard.
svienot Dec 22, 2023
3848dae
Display bytecode and assembly code side-by-side.
svienot Dec 22, 2023
e524a4d
Adjust unit tests.
svienot Dec 22, 2023
9519583
Show FE opcode as 'INVALID' (white) and unknown opcodes as 'invalid' …
svienot Dec 26, 2023
131af9f
Check bytecode and assembly code in unit tests.
svienot Dec 26, 2023
75dbbc2
ByteCodeValue is not copyable. Don't change background on hover when …
svienot Dec 26, 2023
8ca7bb4
Fix typo
svienot Dec 26, 2023
e1dec69
Make proper use of contract and account caches in OpcodeValue.
svienot Dec 26, 2023
aa1de01
Remove debug traces.
svienot Dec 26, 2023
48da731
Save 'Show hexa opcode' user pref in local storage.
svienot Dec 26, 2023
b62b83d
Extend OpcodeValue unit tests.
svienot Dec 26, 2023
2abff17
Remove test code.
svienot Dec 26, 2023
968f38d
Remove declaration of module vue-prism-component (not needed by disas…
svienot Dec 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/AppStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ export class AppStorage {
AppStorage.createCookie(AppStorage.COOKIE_POLICY_NAME, policy, AppStorage.COOKIE_POLICY_VALIDITY)
}

//
// display hexa opcodes in assembly code
//

private static readonly SHOW_HEXA_OPCODE_KEY = 'hexaOpcode'

public static getShowHexaOpcode(): boolean {
return this.getLocalStorageItem(this.SHOW_HEXA_OPCODE_KEY) != null
}

public static setShowHexaOpcode(newValue: boolean|null): void {
this.setLocalStorageItem(this.SHOW_HEXA_OPCODE_KEY, newValue ? "true" : null)
}

//
// Private
Expand Down
2 changes: 1 addition & 1 deletion src/components/Copyable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<template>
<div class="shy-scope" style="display: inline-block; position: relative;">
<slot name="content"/>
<div v-if="contentToCopy" id="shyCopyButton" class="shy"
<div v-if="enableCopy && contentToCopy" id="shyCopyButton" class="shy"
style="position: absolute; left: 0; top: 0; width: 100%; height: 100%">
<div style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.50)"></div>
<div v-if="enableCopy"
Expand Down
9 changes: 5 additions & 4 deletions src/components/DashboardCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
<slot name="content"></slot>
</div>

<div class="columns h-is-property-text">
<div class="columns is-multiline h-is-property-text">

<div class="column">
<div class="column is-6-desktop" :class="{'is-full': !isMediumScreen}">
<slot name="leftContent"></slot>
</div>
<div class="column" :class="{'h-has-column-separator':slots.rightContent}">
<div class="column is-6-desktop" :class="{'h-has-column-separator':slots.rightContent&&isMediumScreen}">
<slot name="rightContent"></slot>
</div>

Expand All @@ -69,9 +69,10 @@ export default defineComponent({

setup() {
const isSmallScreen = inject('isSmallScreen', true)
const isMediumScreen = inject('isMediumScreen', true)
const isTouchDevice = inject('isTouchDevice', false)
const slots = useSlots()
return { isSmallScreen, isTouchDevice, slots }
return { isSmallScreen, isMediumScreen, isTouchDevice, slots }
}
})

Expand Down
47 changes: 39 additions & 8 deletions src/components/contract/ContractByteCodeSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,29 @@
</template>
</Property>
<Property id="solcVersion" :full-width="true">
<template v-slot:name>Compiler Version</template>
<template v-slot:name>Solidity Compiler Version</template>
<template v-slot:value>
<StringValue :string-value="solcVersion ?? undefined"/>
</template>
</Property>
<Property id="code" :full-width="true">
<template v-slot:name>Runtime Bytecode</template>
</Property>
<ByteCodeValue :byte-code="byteCode ?? undefined" class="mt-3"/>
<div class="columns is-multiline h-is-property-text">
<div id="bytecode" class="column is-6" :class="{'is-full': !isSmallScreen}">
<p class="has-text-weight-light">Runtime Bytecode</p>
<ByteCodeValue :byte-code="byteCode ?? undefined" class="mt-3 mb-4"/>
</div>
<div id="assembly-code" class="column is-6" :class="{'h-has-column-separator':isSmallScreen}">
<div class="is-flex is-align-items-center is-justify-content-space-between">
<p class="has-text-weight-light">Assembly Bytecode</p>
<div class="is-flex is-align-items-center is-justify-content-end">
<p class="has-text-weight-light">Show hexa opcode</p>
<label class="checkbox pt-1 ml-3">
<input type="checkbox" v-model="showHexaOpcode">
</label>
</div>
</div>
<DisassembledCodeValue :byte-code="byteCode ?? undefined" :show-hexa-opcode="showHexaOpcode"/>
</div>
</div>
</template>
</DashboardCard>

Expand All @@ -102,7 +116,7 @@

<script lang="ts">

import {computed, defineComponent, inject, PropType, ref} from 'vue';
import {computed, defineComponent, inject, onMounted, PropType, ref, watch} from 'vue';
import DashboardCard from "@/components/DashboardCard.vue";
import ByteCodeValue from "@/components/values/ByteCodeValue.vue";
import StringValue from "@/components/values/StringValue.vue";
Expand All @@ -111,14 +125,26 @@ import {ContractAnalyzer} from "@/utils/analyzer/ContractAnalyzer";
import {routeManager} from "@/router";
import InfoTooltip from "@/components/InfoTooltip.vue";
import ContractVerificationDialog from "@/components/verification/ContractVerificationDialog.vue";
import DisassembledCodeValue from "@/components/values/DisassembledCodeValue.vue";
import HexaValue from "@/components/values/HexaValue.vue";
import {AppStorage} from "@/AppStorage";

const FULL_MATCH_TOOLTIP = `A Full Match indicates that the bytecode of the deployed contract is byte-by-byte the same as the compilation output of the given source code files with the settings defined in the metadata file. This means the contents of the source code files and the compilation settings are exactly the same as when the contract author compiled and deployed the contract.`
const PARTIAL_MATCH_TOOLTIP = `A Partial Match indicates that the bytecode of the deployed contract is the same as the compilation output of the given source code files except for the metadata hash. This means the deployed contract and the given source code + metadata function in the same way but there are differences in source code comments, variable names, or other metadata fields such as source paths.`

export default defineComponent({
name: 'ContractByteCodeSection',

components: {ContractVerificationDialog, InfoTooltip, Property, StringValue, ByteCodeValue, DashboardCard},
components: {
HexaValue,
DisassembledCodeValue,
ContractVerificationDialog,
InfoTooltip,
Property,
StringValue,
ByteCodeValue,
DashboardCard
},

props: {
contractAnalyzer: {
Expand Down Expand Up @@ -154,6 +180,10 @@ export default defineComponent({

const tooltipText = computed(() => isFullMatch.value ? FULL_MATCH_TOOLTIP : PARTIAL_MATCH_TOOLTIP)

const showHexaOpcode = ref(false)
onMounted(() => showHexaOpcode.value = AppStorage.getShowHexaOpcode())
watch(showHexaOpcode, () => AppStorage.setShowHexaOpcode(showHexaOpcode.value ? showHexaOpcode.value : null))

return {
isTouchDevice,
isSmallScreen,
Expand All @@ -169,7 +199,8 @@ export default defineComponent({
contractId: props.contractAnalyzer.contractId,
byteCodeAnalyzer: props.contractAnalyzer.byteCodeAnalyzer,
verifyDidComplete,
isFullMatch
isFullMatch,
showHexaOpcode,
}
}
});
Expand Down
54 changes: 33 additions & 21 deletions src/components/values/ByteCodeValue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@

<template>

<textarea v-if="textValue"
v-model="textValue"
readonly rows="4"
style="width:100%; font-family: novamonoregular,monospace"></textarea>
<div v-if="textValue" id="bytecode"
class="pl-1 mt-2 mr-1 code-data-box">
<HexaValue :byte-string="textValue" :copyable="false"/>
</div>

<span v-else-if="initialLoading"/>
<span v-else-if="initialLoading"/>

<span v-else class="has-text-grey">None</span>
<span v-else class="has-text-grey">None</span>

</template>

Expand All @@ -43,22 +43,24 @@

import {defineComponent, inject, ref, watch} from 'vue';
import {initialLoadingKey} from "@/AppKeys";
import HexaValue from "@/components/values/HexaValue.vue";

export default defineComponent({
name: 'ByteCodeValue',

props: {
byteCode: String,
},

setup(props) {
const textValue = ref(props.byteCode)
watch(() => props.byteCode, () => {
textValue.value = props.byteCode
})
const initialLoading = inject(initialLoadingKey, ref(false))
return { textValue, initialLoading }
}
name: 'ByteCodeValue',
components: {HexaValue},

props: {
byteCode: String,
},

setup(props) {
const textValue = ref(props.byteCode)
watch(() => props.byteCode, () => {
textValue.value = props.byteCode
})
const initialLoading = inject(initialLoadingKey, ref(false))
return {textValue, initialLoading}
}
});

</script>
Expand All @@ -67,4 +69,14 @@ export default defineComponent({
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style/>
<style>

.code-data-box {
border: 0.5px solid dimgrey;
gap: 0.42rem;
max-height: 20rem;
overflow-y: auto;
min-height: 5rem
}

</style>
107 changes: 107 additions & 0 deletions src/components/values/DisassembledCodeValue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!--
-
- Hedera Mirror Node Explorer
-
- Copyright (C) 2021 - 2023 Hedera Hashgraph, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-->

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- TEMPLATE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>
<div v-if="disassembly" id="disassembly" class="mt-2 p-2 is-flex analyzed-data-box">
<div v-for="opcode in disassembly" v-if="disassembly && disassembly.length > 0" :key="opcode.index16">
<OpcodeValue :opcode="opcode" :show-hexa-opcode="showHexaOpcode"/>
</div>
<p v-else class="has-text-grey is-italic has-text-weight-medium">{{ disassembledError }}</p>
</div>

<span v-else-if="initialLoading"/>

<span v-else class="has-text-grey">None</span>

</template>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts">

import {computed, defineComponent, inject, ref} from 'vue';
import {Disassembler} from '@/utils/bytecode_tools/disassembler/BytecodeDisassembler'
import {DisassembledOpcodeOutput} from '@/utils/bytecode_tools/disassembler/utils/helpers';
import OpcodeValue from "@/components/values/OpcodeValue.vue";
import {initialLoadingKey} from "@/AppKeys";

export default defineComponent({
name: 'DisassembledCodeValue',
components: {OpcodeValue},

props: {
byteCode: {
type: String,
default: ""
},
showHexaOpcode: {
type: Boolean,
default: false
}
},

setup(props) {
const initialLoading = inject(initialLoadingKey, ref(false))

const isValidBytecode = computed(() => {
const BYTECODE_REGEX = /^(0x)?([0-9a-fA-F]{2})+$/;
return BYTECODE_REGEX.test(props.byteCode)
})

const disassembly = computed<DisassembledOpcodeOutput[] | null>(
() => isValidBytecode ? Disassembler.disassemble(props.byteCode) : null)

const disassembledError = computed<string | null>(() =>
isValidBytecode ? null : (props.byteCode === "" ? "No data found..." : "Invalid bytecode")
)

return {
initialLoading,
disassembly,
disassembledError,
}
}
});

</script>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style>

.analyzed-data-box {
border: 0.5px solid dimgrey;
gap: 0.42rem;
flex-direction: column;
max-height: 20rem;
overflow-y: auto;
font-family: novamonoregular, monospace;
min-height: 5rem
}

</style>
6 changes: 5 additions & 1 deletion src/components/values/HexaValue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export default defineComponent({
wordWrapSmall: {
type: Number,
default: null
},
copyable: {
type:Boolean,
default: true
}
},

Expand Down Expand Up @@ -101,7 +105,7 @@ export default defineComponent({
}

const isCopyEnabled = computed(() => {
return (normByteString.value?.length ?? 0) >= 1
return props.copyable && (normByteString.value?.length ?? 0) >= 1
})

// 4)
Expand Down
Loading