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: initializing conditional stack opcodes #32

Closed
wants to merge 19 commits into from

Conversation

okhaimie-dev
Copy link
Contributor

@okhaimie-dev okhaimie-dev commented Sep 11, 2024

Implement conditional opcodes and wip to enhance script code structure

Added the following opcodes with corresponding tests:

  • opIf
  • opNotIf
  • opElse
  • opEndIf

Others

  • opReserved
  • opVer

Improvements:

  • Structured opcode implementation to align with Shinigami's approach for better maintainability and collaboration
  • Introduced cond_stack for managing conditional execution

This commit addresses issue #29

src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Show resolved Hide resolved
if (self.stack.items.len == 0) {
return ConditionalStackError.EmptyConditionalStack;
}
_ = self.stack.pop();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you return the value?
Should it be named delete then? Not pop

src/script/cond_stack.zig Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow.zig Outdated Show resolved Hide resolved
@tdelabro
Copy link
Collaborator

@okhaimie-dev don't forget to ask for a new review when you are finished pushing your changes

src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/engine.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow_control.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow_control.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow_control.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow_control.zig Outdated Show resolved Hide resolved
src/script/opcodes/flow_control.zig Outdated Show resolved Hide resolved
src/script/cond_stack.zig Outdated Show resolved Hide resolved
Comment on lines 55 to 58
///
/// Returns:
/// The popped value (u8) if the stack is not empty
/// ConditionalStackError.EmptyConditionalStack if the stack is empty
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we know what pop return. You can remove this long comment

/// Push a conditional value onto the stack
///
/// Args:
/// value: The conditional value to push (typically 0, 1, or 2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be something else than 0, 1 or 2? You comment suggest it can

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can you define an enum for more readability

	OpCondFalse = 0
	OpCondTrue  = 1
	OpCondSkip  = 2

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be something else than 0, 1 or 2? You comment suggest it can

Reading other implementations it looks like it cannot. So please change your comment to not mean that it could

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And ofcourse use those OpCond enum in your tests rather than 1, 2, 3

if (self.stack.items.len == 0) {
return ConditionalStackError.UnbalancedConditional;
}
_ = self.stack.pop();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you want to return the poped value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdelabro, I don't see any need to pop it is possible to remove the value completely. what do you think?

@@ -391,3 +391,27 @@ pub fn isUnnamedPushNDataOpcode(opcode: Opcode) ?u8 {
const opcodeByte = opcode.toBytes();
return if (opcodeByte > 0x00 and opcodeByte <= 0x4b) opcodeByte else null;
}

/// Conditional execution constants for script operations.
pub const OpConditional = enum(u8) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are not Opcodes so I don't like the name.
What about "CondStackValue" ?

///
/// Returns:
/// Possible error if allocation fails
pub fn push(self: *Self, value: u8) !void {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if values are strictly limited, change the signature to reflect that:

pub fn push(self: *Self, value: CondStackValue) !void

const Self = @This();

/// Dynamic array to store conditional values
stack: std.ArrayList(u8),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std.ArrayList(CondStackValue) is more restrictive and more expressive

@tdelabro
Copy link
Collaborator

@okhaimie-dev are you still working on this?

@okhaimie-dev
Copy link
Contributor Author

@okhaimie-dev are you still working on this?

I'll send a fix tomorrow, I have been traveling

@tdelabro
Copy link
Collaborator

tdelabro commented Oct 3, 2024

@okhaimie-dev how is it going?

@KhairallahA
Copy link
Contributor

@tdelabro It's already covered here I didn't notice this but I don't understand what the problem is that is still being faced here to now?

@tdelabro
Copy link
Collaborator

tdelabro commented Oct 3, 2024

@KhairallahA there are a few comments I did that have to be fixed

var engine = Engine.init(allocator, script, ScriptFlags{});
defer engine.deinit();

try engine.stack.pushBool(true); // Push a true value onto the stack
Copy link
Contributor

@KhairallahA KhairallahA Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is necessary to push bool values.

OP_0 = FALSE
OP_1 = TRUE

OP_1 should be the value that returns TRUE and OP_0 should return FALSE.

(According to what is written in the ops implementation, let me know if there is a use case like this, I may be wrong)


// Test OP_ENDIF with matching OP_IF
{
const script_bytes = [_]u8{ Opcode.OP_IF.toBytes(), Opcode.OP_ENDIF.toBytes() };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to run opcodes functions directly to test it, you can test OP_ENDIF like this:

 const script_bytes = [_]u8{
   Opcode.OP_0.toBytes(),
   Opcode.OP_IF.toBytes(),
   Opcode.OP_1.toBytes(),
   Opcode.OP_ENDIF.toBytes(),
};

What we expect:

engine.stack.len() == 0

if (self.stack.items.len == 0) {
return ConditionalStackError.UnbalancedConditional;
}
_ = self.stack.pop();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdelabro, I don't see any need to pop it is possible to remove the value completely. what do you think?

@KhairallahA
Copy link
Contributor

KhairallahA commented Oct 3, 2024

@KhairallahA there are a few comments I did that have to be fixed

I think the problem is in understanding the function of Flow control because the complexity in running opcodes execute, not ops themselves

https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script#Flow_control

@okhaimie-dev, I always think it is better to go through these steps for implementation:

  • Implement the main goal (as it was) so that everything is clear.
  • Improve the implementation method in the best possible way with the help from @tdelabro.

It is possible to implement cond_stack by replacing the use of u8 with bool, but we will need to use loop, as the implementation method is many, but what matters in the beginning is achieving the main goal of the implementation process so that the improvement becomes easier

@okhaimie-dev
Copy link
Contributor Author

Alright, thanks. Working on this rn

@KhairallahA
Copy link
Contributor

KhairallahA commented Oct 3, 2024

@okhaimie-dev let me know if there's any problem you face
you can close it for the day, you have done a lot.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding these opcodes in engine file? No need to clutter your mind like that, better have them all in one place.

Comment on lines +406 to +416
/// Convert the enum to its underlying u8 value
pub fn toU8(self: OpConditional) u8 {
return @intFromEnum(self);
}

/// Create an OpConditional from a u8 value
pub fn fromU8(value: u8) !OpConditional {
return std.meta.intToEnum(OpConditional, value) catch {
return error.InvalidOpConditional;
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using the @tdelabro method to include CondStackValue as a data type as an alternative to u8, you will not need to use these functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhairallahA if I don't make a PR in the next 24hrs, you can take over this. I am still trying to get settled and not had enough time to dig into it so far.

cc: @tdelabro

@tdelabro
Copy link
Collaborator

tdelabro commented Oct 7, 2024

let's close this as the project is put on hold. I will still pay you something for your work on OnlyDust, ping me if I forget

@tdelabro tdelabro closed this Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants