diff --git a/package.json b/package.json index c392f3e..e2d16f3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "devDependencies": { - "@bitauth/libauth": "3.1.0-next.0", + "@bitauth/libauth": "3.1.0-next.1", "@blueprintjs/core": "^5.8.2", "@blueprintjs/icons": "^5.7.0", "@blueprintjs/select": "^5.0.23", diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 1153a8d..fe1251d 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -190,7 +190,7 @@ export const Editor = connect( deleteScript={props.deleteScript} editScript={props.editScript} frame={computed.scriptEditorFrames[indexFromTop]!} - isP2SH={computed.lockingType === 'p2sh20'} + lockingType={computed.lockingType} isPushed={computed.isPushed} scriptDetails={computed.scriptDetails} setCursorLine={setCursorLine[indexFromTop]!} diff --git a/src/editor/dialogs/edit-script-dialog/EditScriptDialog.tsx b/src/editor/dialogs/edit-script-dialog/EditScriptDialog.tsx index 95f229f..195f35b 100644 --- a/src/editor/dialogs/edit-script-dialog/EditScriptDialog.tsx +++ b/src/editor/dialogs/edit-script-dialog/EditScriptDialog.tsx @@ -1,6 +1,11 @@ import '../editor-dialog.css'; import { ActionCreators } from '../../../state/reducer'; -import { ScriptType } from '../../../state/types'; +import { + LockingType, + lockingTypeDescriptions, + lockingTypes, + ScriptType, +} from '../../../state/types'; import { toConventionalId } from '../../common'; import { @@ -9,6 +14,7 @@ import { Classes, Dialog, FormGroup, + HTMLSelect, InputGroup, Intent, Switch, @@ -21,7 +27,7 @@ export const EditScriptDialog = ({ name, internalId, id, - isP2SH, + lockingType, isPushed, isOpen, closeDialog, @@ -33,8 +39,8 @@ export const EditScriptDialog = ({ name: string; internalId: string; id: string; - isP2SH?: boolean; - isPushed?: boolean; + lockingType: LockingType; + isPushed: boolean; usedIds: string[]; editScript: typeof ActionCreators.editScript; deleteScript: typeof ActionCreators.deleteScript; @@ -43,7 +49,7 @@ export const EditScriptDialog = ({ }) => { const [scriptName, setScriptName] = useState(name); const [scriptId, setScriptId] = useState(id); - const [scriptIsP2SH, setScriptIsP2SH] = useState(isP2SH); + const [scriptLockingType, setScriptLockingType] = useState(lockingType); const [scriptIsPushed, setScriptIsPushed] = useState(isPushed); const [nonUniqueId, setNonUniqueId] = useState(''); const [promptDelete, setPromptDelete] = useState(false); @@ -55,7 +61,7 @@ export const EditScriptDialog = ({ onOpening={() => { setScriptName(name); setScriptId(id); - setScriptIsP2SH(isP2SH); + setScriptLockingType(lockingType); setNonUniqueId(''); }} onClose={() => { @@ -117,22 +123,16 @@ export const EditScriptDialog = ({ )} {scriptType === 'locking' && ( - If enabled, this script will be nested in the standard P2SH - template. - - } + helperText={lockingTypeDescriptions[scriptLockingType]} label="Script Mode" labelFor="script-p2sh" inline={true} > - { - setScriptIsP2SH(!scriptIsP2SH); + options={lockingTypes} + onChange={(e) => { + setScriptLockingType(e.currentTarget.value as LockingType); }} /> @@ -212,7 +212,7 @@ export const EditScriptDialog = ({ scriptName === '' || (scriptName === name && scriptId === id && - scriptIsP2SH === isP2SH && + scriptLockingType === lockingType && scriptIsPushed === isPushed) || (!isTest && scriptId === '') } @@ -224,7 +224,7 @@ export const EditScriptDialog = ({ internalId, name: scriptName, id: scriptId, - lockingType: scriptIsP2SH ? 'p2sh20' : 'standard', + lockingType: scriptLockingType, isPushed: scriptIsPushed, }); closeDialog(); diff --git a/src/editor/dialogs/new-script-dialog/NewScriptDialog.tsx b/src/editor/dialogs/new-script-dialog/NewScriptDialog.tsx index 52fe1bf..07b6202 100644 --- a/src/editor/dialogs/new-script-dialog/NewScriptDialog.tsx +++ b/src/editor/dialogs/new-script-dialog/NewScriptDialog.tsx @@ -4,7 +4,8 @@ import { ActiveDialog, BaseScriptType, CurrentScripts, - ScriptType, + scriptTypes, + typeDescriptions, } from '../../../state/types'; import { createInsecureUuidV4 } from '../../../utils'; import { toConventionalId } from '../../common'; @@ -20,28 +21,6 @@ import { import { WarningSign } from '@blueprintjs/icons'; import React, { useState } from 'react'; -const scriptTypes: { label: string; value: ScriptType }[] = [ - { label: 'Locking Script', value: 'locking' }, - { label: 'Unlocking Script', value: 'unlocking' }, - { label: 'Isolated Script', value: 'isolated' }, - { label: 'Script Test', value: 'test-setup' }, -]; - -const typeDescriptions: { [key in ScriptType]: string } = { - locking: - 'Locking scripts hold funds. A locking script is the “challenge” which must be unlocked to spend a transaction output. An “Address” is simply an abstraction for a specific locking script.', - unlocking: - 'An unlocking script spends from a locking script. To create a transaction, the spender must provide a valid unlocking script for each input being spent. (A locking script can be unlocked by multiple unlocking scripts.)', - isolated: - 'An isolated script is useful for constructions like checksums or re-usable utility scripts (which can be used inside other scripts). Isolated scripts can have script tests, e.g. utility scripts can be tested to ensure they perform a series of operations properly.', - 'test-setup': - 'A script test is applied to an isolated script. Each script test has a “setup” phase which is evaluated before the tested script, and a “check” phase which is evaluated after. The test passes if the “check” script leaves a single Script Number 1 on the stack.', - tested: - 'Something is broken: tested scripts should be created by assigning a test-setup script to an isolated script.', - 'test-check': - 'Something is broken: script tests should use the `test-setup` type in this dialog.', -}; - const hasParent = (scriptType: BaseScriptType) => scriptType === 'unlocking' || scriptType === 'test-setup'; diff --git a/src/editor/editor-state.ts b/src/editor/editor-state.ts index d4cf02e..3445266 100644 --- a/src/editor/editor-state.ts +++ b/src/editor/editor-state.ts @@ -26,6 +26,8 @@ import { CompilationResultSuccess, createCompiler, createVirtualMachineBch2023, + createVirtualMachineBch2025, + createVirtualMachineBch2026, createVirtualMachineBchSpec, encodeDataPush, EvaluationSample, @@ -106,7 +108,11 @@ export const computeEditorState = < const vm = state.currentVmId === 'BCH_2023_05' ? createVirtualMachineBch2023() - : createVirtualMachineBchSpec(); + : state.currentVmId === 'BCH_2025_05' + ? createVirtualMachineBch2025() + : state.currentVmId === 'BCH_2026_05' + ? createVirtualMachineBch2026() + : createVirtualMachineBchSpec(); const compiler = createCompiler(configuration); /** diff --git a/src/editor/evaluation-viewer/EvaluationViewer.tsx b/src/editor/evaluation-viewer/EvaluationViewer.tsx index 3162130..443569d 100644 --- a/src/editor/evaluation-viewer/EvaluationViewer.tsx +++ b/src/editor/evaluation-viewer/EvaluationViewer.tsx @@ -104,6 +104,17 @@ const stackItem = ( ); +const elideAt = 200; +const splitAt = 100; +const elideDigits = (digits: string) => + digits.length < elideAt + ? digits + : `${digits.slice(0, splitAt)} \u2026 (${digits.length - elideAt} total digits) \u2026 ${digits.slice(-splitAt)}`; +const elideHex = (characters: string) => + characters.length < elideAt + ? characters + : `${characters.slice(0, splitAt)} \u2026 (${(characters.length - 2) / 2} total bytes) \u2026 ${characters.slice(-splitAt)}`; + const getStackItemDisplaySettings = ( item: Uint8Array, settings: EvaluationViewerSettings, @@ -122,7 +133,7 @@ const getStackItemDisplaySettings = ( const number = vmNumberToBigInt(item, { maximumVmNumberByteLength: settings.vmNumbersDisplayFormat === 'bigint' - ? 258 + ? 10_000 : settings.supportBigInt ? 19 : 8, @@ -132,17 +143,17 @@ const getStackItemDisplaySettings = ( settings.vmNumbersDisplayFormat === 'integer' || settings.vmNumbersDisplayFormat === 'bigint' ) { - return { hex, type: 'number' as const, label: `${number}` }; + return { hex, type: 'number' as const, label: elideDigits(`${number}`) }; } if (settings.vmNumbersDisplayFormat === 'binary') { return { hex, type: 'binary' as const, - label: `0b${binToBinString(item)}`, + label: elideDigits(`0b${binToBinString(item)}`), }; } } - return { hex, type: 'hex' as const, label: hex }; + return { hex, type: 'hex' as const, label: elideHex(hex) }; }; // TODO: modernize @@ -378,7 +389,7 @@ const EvaluationLine = ({ ); return stackItem( itemIndex, - hex, + elideHex(hex), {settings.abbreviateLongStackItems ? abbreviateStackItem(label) @@ -654,7 +665,7 @@ export const ViewerControls = ({ ) : evaluationViewerSettings.vmNumbersDisplayFormat === 'bigint' ? ( diff --git a/src/editor/script-editor/ScriptEditor.css b/src/editor/script-editor/ScriptEditor.css index 6ab656a..a1cfe47 100644 --- a/src/editor/script-editor/ScriptEditor.css +++ b/src/editor/script-editor/ScriptEditor.css @@ -47,16 +47,15 @@ .script-tag { font-size: 0.7em; - background-color: #2e7105; border-radius: 3px; display: inline-block; padding: 0.1em 0.5em; margin-left: 1em; } - .p2sh-tag { + .locking-type-tag { text-transform: uppercase; - background-color: #2e7105; + background-color: var(--editor-background-color); } .pushed-tag { diff --git a/src/editor/script-editor/ScriptEditor.tsx b/src/editor/script-editor/ScriptEditor.tsx index be8e853..0e5c628 100644 --- a/src/editor/script-editor/ScriptEditor.tsx +++ b/src/editor/script-editor/ScriptEditor.tsx @@ -3,6 +3,8 @@ import { MonacoMarkerDataRequired } from '../../cash-assembly/editor-tooling'; import { ActionCreators } from '../../state/reducer'; import { IDESupportedProgramState, + LockingType, + lockingTypeDescriptions, ScriptDetails, VariableDetails, } from '../../state/types'; @@ -202,7 +204,7 @@ const updateMarkers = export const ScriptEditor = (props: { frame: ScriptEditorFrame; - isP2SH: boolean; + lockingType: LockingType; isPushed: boolean; scriptDetails: ScriptDetails; variableDetails: VariableDetails; @@ -803,12 +805,26 @@ export const ScriptEditor = (props: { {name} {scriptType === 'test-setup' &&  (Setup)} {scriptType === 'test-check' &&  (Check)} - {props.isP2SH && ( + {props.lockingType === 'p2sh20' ? ( - P2SH + P2SH20 + + ) : props.lockingType === 'p2sh32' ? ( + + P2SH32 + + ) : ( + + bare )} {props.isPushed && scriptType === 'tested' && ( @@ -870,7 +886,7 @@ export const ScriptEditor = (props: { id={id} name={name} scriptType={scriptType} - isP2SH={props.isP2SH} + lockingType={props.lockingType} isPushed={props.isPushed} closeDialog={() => { setEditScriptDialogIsOpen(false); diff --git a/src/header/HeaderBar.tsx b/src/header/HeaderBar.tsx index 93481ba..4a83b68 100644 --- a/src/header/HeaderBar.tsx +++ b/src/header/HeaderBar.tsx @@ -47,8 +47,8 @@ const ideModes: IDESupportedModes[] = [ const vms: IDESupportedVirtualMachine[] = [ { id: 'BCH_2023_05', name: 'BCH 2023 VM', disabled: false }, - // { id: 'BCH_2025_05', name: 'BCH 2025 VM', disabled: false }, - // { id: 'BCH_2026_05', name: 'BCH 2026 VM', disabled: false }, + { id: 'BCH_2025_05', name: 'BCH 2025 VM', disabled: false }, + { id: 'BCH_2026_05', name: 'BCH 2026 VM', disabled: false }, { id: 'BCH_SPEC', name: 'BCH SPEC VM', disabled: false }, { id: 'BTC_2017_08', name: 'BTC 2017 VM', disabled: true }, { id: 'BSV_2020_02', name: 'BSV 2020 VM', disabled: true }, diff --git a/src/state/defaults.ts b/src/state/defaults.ts index 8574d2d..e0e9d63 100644 --- a/src/state/defaults.ts +++ b/src/state/defaults.ts @@ -15,7 +15,12 @@ export const emptyTemplate: WalletTemplate = { name: 'Untitled', entities: {}, scripts: {}, - supported: ['BCH_2023_05', 'BCH_SPEC'] as IDESupportedVM[], + supported: [ + 'BCH_2023_05', + 'BCH_2025_05', + 'BCH_2026_05', + 'BCH_SPEC', + ] as IDESupportedVM[], version: 0 as const, }; diff --git a/src/state/reducer.ts b/src/state/reducer.ts index 5b89d67..02ca9ea 100644 --- a/src/state/reducer.ts +++ b/src/state/reducer.ts @@ -527,7 +527,7 @@ class App extends ImmerReducer { ) { this.draftState.currentVmId = firstSupportedVm; this.draftState.evaluationViewerSettings.supportBigInt = - firstSupportedVm === 'BCH_SPEC'; + firstSupportedVm !== 'BCH_2023_05'; } this.draftState.templateLoadTime = new Date(); this.draftState.currentTemplate = template; @@ -559,7 +559,8 @@ class App extends ImmerReducer { } activateVm(vm: IDESupportedVM) { this.draftState.currentVmId = vm; - this.draftState.evaluationViewerSettings.supportBigInt = vm === 'BCH_SPEC'; + this.draftState.evaluationViewerSettings.supportBigInt = + vm !== 'BCH_2023_05'; } } diff --git a/src/state/types.ts b/src/state/types.ts index 38d54c6..2232ce6 100644 --- a/src/state/types.ts +++ b/src/state/types.ts @@ -115,6 +115,47 @@ export type ScenarioDetails = { export type ScriptType = BaseScriptType | 'tested' | 'test-check'; +export const scriptTypes: { label: string; value: ScriptType }[] = [ + { label: 'Locking Script', value: 'locking' }, + { label: 'Unlocking Script', value: 'unlocking' }, + { label: 'Isolated Script', value: 'isolated' }, + { label: 'Script Test', value: 'test-setup' }, +]; + +export const typeDescriptions: { [key in ScriptType]: string } = { + locking: + 'Locking scripts hold funds. A locking script is the “challenge” which must be unlocked to spend a transaction output. An “Address” is simply an abstraction for a specific locking script.', + unlocking: + 'An unlocking script spends from a locking script. To create a transaction, the spender must provide a valid unlocking script for each input being spent. (A locking script can be unlocked by multiple unlocking scripts.)', + isolated: + 'An isolated script is useful for constructions like checksums or re-usable utility scripts (which can be used inside other scripts). Isolated scripts can have script tests, e.g. utility scripts can be tested to ensure they perform a series of operations properly.', + 'test-setup': + 'A script test is applied to an isolated script. Each script test has a “setup” phase which is evaluated before the tested script, and a “check” phase which is evaluated after. The test passes if the “check” script leaves a single Script Number 1 on the stack.', + tested: + 'Something is broken: tested scripts should be created by assigning a test-setup script to an isolated script.', + 'test-check': + 'Something is broken: script tests should use the `test-setup` type in this dialog.', +}; + +export type LockingType = WalletTemplateScriptLocking['lockingType']; + +export const lockingTypes: { label: string; value: LockingType }[] = [ + { label: 'P2SH20', value: 'p2sh20' }, + { label: 'P2SH32', value: 'p2sh32' }, + { label: 'Bare (non-P2SH)', value: 'standard' }, +]; + +export const lockingTypeDescriptions: { + [key in LockingType]: string; +} = { + p2sh20: + 'This is a P2SH20 script: P2SH20-wrapping bytecode is automatically included during compilation.', + p2sh32: + 'This is a P2SH32 script. The P2SH32-wrapping bytecode is automatically included during compilation.', + standard: + 'This is a bare (non-P2SH) script: it is compiled directly into the transaction output without P2SH-wrapping bytecode.', +}; + export type BaseScriptType = | 'locking' | 'unlocking'