defmodule BitcoinLib.Script.OpcodeManager do
@moduledoc """
Converts back and forts from script to opcode list
"""
alias BitcoinLib.Script.Opcodes.Arithmetic
alias BitcoinLib.Script.Opcodes.{
Arithmetic,
BitwiseLogic,
Constants,
Crypto,
FlowControl,
Stack,
Locktime,
Reserved,
Splice,
PseudoWords,
Data
}
@byte 8
@zero Constants.Zero.v()
@one Constants.One.v()
@one_negate Constants.OneNegate.v()
@two Constants.Two.v()
@three Constants.Three.v()
@four Constants.Four.v()
@five Constants.Five.v()
@six Constants.Six.v()
@seven Constants.Seven.v()
@eight Constants.Eight.v()
@nine Constants.Nine.v()
@ten Constants.Ten.v()
@eleven Constants.Eleven.v()
@twelve Constants.Twelve.v()
@thirteen Constants.Thirteen.v()
@fourteen Constants.Fourteen.v()
@fifteen Constants.Fifteen.v()
@sixteen Constants.Sixteen.v()
@push_data_1 Constants.PushData1.v()
@push_data_2 Constants.PushData2.v()
@push_data_4 Constants.PushData4.v()
@nop FlowControl.Nop.v()
@if FlowControl.If.v()
@else_ FlowControl.Else.v()
@end_if FlowControl.EndIf.v()
@verify FlowControl.Verify.v()
@return FlowControl.Return.v()
@to_alt_stack Stack.ToAltStack.v()
@from_alt_stack Stack.FromAltStack.v()
@if_dup Stack.IfDup.v()
@depth Stack.Depth.v()
@drop Stack.Drop.v()
@dup Stack.Dup.v()
@rot Stack.Rot.v()
@swap Stack.Swap.v()
@two_dup Stack.TwoDup.v()
@two_over Stack.TwoOver.v()
@two_swap Stack.TwoSwap.v()
@size Splice.Size.v()
@equal BitwiseLogic.Equal.v()
@equal_verify BitwiseLogic.EqualVerify.v()
@one_add Arithmetic.OneAdd.v()
@one_sub Arithmetic.OneSub.v()
@sub Arithmetic.Sub.v()
@bool_or Arithmetic.BoolOr.v()
@less_than Arithmetic.LessThan.v()
@greater_than Arithmetic.GreaterThan.v()
@negate Arithmetic.Negate.v()
@abs Arithmetic.Abs.v()
@not_ Arithmetic.Not.v()
@min Arithmetic.Min.v()
@within Arithmetic.Within.v()
@ripemd160 Crypto.Ripemd160.v()
@sha1 Crypto.Sha1.v()
@sha256 Crypto.Sha256.v()
@hash160 Crypto.Hash160.v()
@hash256 Crypto.Hash256.v()
@code_separator Crypto.CodeSeparator.v()
@check_sig Crypto.CheckSig.v()
@check_sig_verify Crypto.CheckSigVerify.v()
@check_multi_sig Crypto.CheckMultiSig.v()
@check_multi_sig_verify Crypto.CheckMultiSigVerify.v()
@check_lock_time_verify Locktime.CheckLockTimeVerify.v()
@nop1 Reserved.Nop1.v()
@invalid_opcode PseudoWords.InvalidOpcode.v()
alias BitcoinLib.Signing.Psbt.CompactInteger
alias BitcoinLib.Script.Opcodes.Data
@doc """
Encode an opcode into a bitstring
"""
@spec encode_opcode(any) :: bitstring()
def encode_opcode(%Constants.Zero{}) do
Constants.Zero.encode()
end
def encode_opcode(%Constants.One{}) do
Constants.One.encode()
end
def encode_opcode(%Constants.OneNegate{}) do
Constants.OneNegate.encode()
end
def encode_opcode(%Constants.Two{}) do
Constants.Two.encode()
end
def encode_opcode(%Constants.Three{}) do
Constants.Three.encode()
end
def encode_opcode(%Constants.Four{}) do
Constants.Four.encode()
end
def encode_opcode(%Constants.Five{}) do
Constants.Five.encode()
end
def encode_opcode(%Constants.Six{}) do
Constants.Six.encode()
end
def encode_opcode(%Constants.Seven{}) do
Constants.Seven.encode()
end
def encode_opcode(%Constants.Eight{}) do
Constants.Eight.encode()
end
def encode_opcode(%Constants.Nine{}) do
Constants.Nine.encode()
end
def encode_opcode(%Constants.Ten{}) do
Constants.Ten.encode()
end
def encode_opcode(%Constants.Eleven{}) do
Constants.Eleven.encode()
end
def encode_opcode(%Constants.Twelve{}) do
Constants.Twelve.encode()
end
def encode_opcode(%Constants.Thirteen{}) do
Constants.Thirteen.encode()
end
def encode_opcode(%Constants.Fourteen{}) do
Constants.Fourteen.encode()
end
def encode_opcode(%Constants.Fifteen{}) do
Constants.Fifteen.encode()
end
def encode_opcode(%Constants.Sixteen{}) do
Constants.Sixteen.encode()
end
def encode_opcode(%Constants.PushData1{value: value}) do
opcode = Constants.PushData1.encode()
data = Data.encode(%Data{value: value})
<<opcode::bitstring-8, data::bitstring>>
end
def encode_opcode(%Constants.PushData2{value: value}) do
opcode = Constants.PushData2.encode()
data = Data.encode(%Data{value: value})
<<opcode::bitstring-8, data::bitstring>>
end
def encode_opcode(%Constants.PushData4{value: value}) do
opcode = Constants.PushData4.encode()
data = Data.encode(%Data{value: value})
<<opcode::bitstring-8, data::bitstring>>
end
def encode_opcode(%Stack.ToAltStack{}) do
Stack.ToAltStack.encode()
end
def encode_opcode(%Stack.FromAltStack{}) do
Stack.FromAltStack.encode()
end
def encode_opcode(%Stack.IfDup{}) do
Stack.IfDup.encode()
end
def encode_opcode(%Stack.Depth{}) do
Stack.Depth.encode()
end
def encode_opcode(%Stack.Drop{}) do
Stack.Drop.encode()
end
def encode_opcode(%Stack.Dup{}) do
Stack.Dup.encode()
end
def encode_opcode(%Stack.Rot{}) do
Stack.Rot.encode()
end
def encode_opcode(%Stack.Swap{}) do
Stack.Swap.encode()
end
def encode_opcode(%Stack.TwoDup{}) do
Stack.TwoDup.encode()
end
def encode_opcode(%Stack.TwoOver{}) do
Stack.TwoOver.encode()
end
def encode_opcode(%Stack.TwoSwap{}) do
Stack.TwoSwap.encode()
end
def encode_opcode(%Splice.Size{}) do
Splice.Size.encode()
end
def encode_opcode(%Crypto.Sha1{}) do
Crypto.Sha1.encode()
end
def encode_opcode(%Crypto.Sha256{}) do
Crypto.Sha256.encode()
end
def encode_opcode(%Crypto.Ripemd160{}) do
Crypto.Ripemd160.encode()
end
def encode_opcode(%Crypto.CodeSeparator{}) do
Crypto.CodeSeparator.encode()
end
def encode_opcode(%Crypto.Hash160{}) do
Crypto.Hash160.encode()
end
def encode_opcode(%Crypto.Hash256{}) do
Crypto.Hash256.encode()
end
def encode_opcode(%FlowControl.If{}) do
FlowControl.If.encode()
end
def encode_opcode(%FlowControl.Else{}) do
FlowControl.Else.encode()
end
def encode_opcode(%FlowControl.EndIf{}) do
FlowControl.EndIf.encode()
end
def encode_opcode(%FlowControl.Nop{}) do
FlowControl.Nop.encode()
end
def encode_opcode(%FlowControl.Verify{}) do
FlowControl.Return.encode()
end
def encode_opcode(%FlowControl.Return{}) do
FlowControl.Return.encode()
end
def encode_opcode(%BitwiseLogic.EqualVerify{}) do
BitwiseLogic.EqualVerify.encode()
end
def encode_opcode(%BitwiseLogic.Equal{}) do
BitwiseLogic.Equal.encode()
end
def encode_opcode(%Arithmetic.OneAdd{}) do
Arithmetic.OneAdd.encode()
end
def encode_opcode(%Arithmetic.OneSub{}) do
Arithmetic.OneSub.encode()
end
def encode_opcode(%Arithmetic.Sub{}) do
Arithmetic.Sub.encode()
end
def encode_opcode(%Arithmetic.BoolOr{}) do
Arithmetic.BoolOr.encode()
end
def encode_opcode(%Arithmetic.LessThan{}) do
Arithmetic.LessThan.encode()
end
def encode_opcode(%Arithmetic.GreaterThan{}) do
Arithmetic.GreaterThan.encode()
end
def encode_opcode(%Arithmetic.Negate{}) do
Arithmetic.Negate.encode()
end
def encode_opcode(%Arithmetic.Abs{}) do
Arithmetic.Abs.encode()
end
def encode_opcode(%Arithmetic.Not{}) do
Arithmetic.Not.encode()
end
def encode_opcode(%Arithmetic.Min{}) do
Arithmetic.Min.encode()
end
def encode_opcode(%Arithmetic.Within{}) do
Arithmetic.Within.encode()
end
def encode_opcode(%Crypto.CheckSig{}) do
Crypto.CheckSig.encode()
end
def encode_opcode(%Crypto.CheckSigVerify{}) do
Crypto.CheckSigVerify.encode()
end
def encode_opcode(%Crypto.CheckMultiSig{}) do
Crypto.CheckMultiSig.encode()
end
def encode_opcode(%Crypto.CheckMultiSigVerify{}) do
Locktime.CheckLockTimeVerify.encode()
end
def encode_opcode(%Locktime.CheckLockTimeVerify{}) do
Crypto.CheckMultiSigVerify.encode()
end
def encode_opcode(%Reserved.Nop1{}) do
Reserved.Nop1.encode()
end
def encode_opcode(%PseudoWords.InvalidOpcode{}) do
PseudoWords.InvalidOpcode.encode()
end
def encode_opcode(%Data{} = data) do
Data.encode(data)
end
@doc """
Extract the opcode on the top of the stack given as an argument
"""
@spec extract_from_script(bitstring(), bitstring()) ::
{:empty_script}
| {:opcode, any(), bitstring()}
| {:data, bitstring(), bitstring()}
| {:error, binary(), bitstring()}
def extract_from_script(<<>>, _whole_script), do: {:empty_script}
def extract_from_script(<<@zero::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Zero{}, remaining}
end
def extract_from_script(<<@one::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.One{}, remaining}
end
def extract_from_script(<<@one_negate::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.OneNegate{}, remaining}
end
def extract_from_script(<<@two::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Two{}, remaining}
end
def extract_from_script(<<@three::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Three{}, remaining}
end
def extract_from_script(<<@four::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Four{}, remaining}
end
def extract_from_script(<<@five::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Five{}, remaining}
end
def extract_from_script(<<@six::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Six{}, remaining}
end
def extract_from_script(<<@seven::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Seven{}, remaining}
end
def extract_from_script(<<@eight::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Eight{}, remaining}
end
def extract_from_script(<<@nine::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Nine{}, remaining}
end
def extract_from_script(<<@ten::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Ten{}, remaining}
end
def extract_from_script(<<@eleven::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Eleven{}, remaining}
end
def extract_from_script(<<@twelve::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Twelve{}, remaining}
end
def extract_from_script(<<@thirteen::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Thirteen{}, remaining}
end
def extract_from_script(<<@fourteen::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Fourteen{}, remaining}
end
def extract_from_script(<<@fifteen::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Fifteen{}, remaining}
end
def extract_from_script(<<@sixteen::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Constants.Sixteen{}, remaining}
end
def extract_from_script(
<<@push_data_1::8, remaining::bitstring>>,
_whole_script
) do
if byte_size(remaining) >= 1 do
<<data_byte_size::8, remaining::bitstring>> = remaining
remaining_size = byte_size(remaining)
if data_byte_size <= remaining_size do
<<data::bitstring-size(data_byte_size * 8), remaining::bitstring>> = remaining
{:opcode, %Constants.PushData1{value: data}, remaining}
else
{
:error,
"OP_PUSHDATA1: trying to get #{data_byte_size} bytes out of a #{remaining_size} bytes binary",
remaining
}
end
else
{:error, "no data while trying to extract a OP_PUSHDATA1", remaining}
end
end
def extract_from_script(
<<@push_data_2::8, remaining::bitstring>>,
_whole_script
) do
if byte_size(remaining) >= 2 do
<<data_byte_size::little-16, remaining::bitstring>> = remaining
remaining_size = byte_size(remaining)
if data_byte_size <= remaining_size do
<<data::bitstring-size(data_byte_size * 8), remaining::bitstring>> = remaining
{:opcode, %Constants.PushData2{value: data}, remaining}
else
{
:error,
"OP_PUSHDATA2: trying to get #{data_byte_size} bytes out of a #{remaining_size} bytes binary",
remaining
}
end
else
{:error, "no data while trying to extract a OP_PUSHDATA2", remaining}
end
end
def extract_from_script(
<<@push_data_4::8, remaining::bitstring>>,
_whole_script
) do
if byte_size(remaining) >= 2 do
<<data_byte_size::little-32, remaining::bitstring>> = remaining
remaining_size = byte_size(remaining)
if data_byte_size <= remaining_size do
<<data::bitstring-size(data_byte_size * 8), remaining::bitstring>> = remaining
{:opcode, %Constants.PushData4{value: data}, remaining}
else
{
:error,
"OP_PUSHDATA4: trying to get #{data_byte_size} bytes out of a #{remaining_size} bytes binary",
remaining
}
end
else
{:error, "no data while trying to extract a OP_PUSHDATA4", remaining}
end
end
def extract_from_script(<<@nop::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.Nop{}, remaining}
end
def extract_from_script(<<@if::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.If{}, remaining}
end
def extract_from_script(<<@else_::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.Else{}, remaining}
end
def extract_from_script(<<@end_if::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.EndIf{}, remaining}
end
def extract_from_script(<<@verify::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.Verify{}, remaining}
end
def extract_from_script(<<@return::8, remaining::bitstring>>, _whole_script) do
{:opcode, %FlowControl.Return{}, remaining}
end
def extract_from_script(<<@to_alt_stack::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.ToAltStack{}, remaining}
end
def extract_from_script(<<@from_alt_stack::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.FromAltStack{}, remaining}
end
def extract_from_script(<<@if_dup::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.IfDup{}, remaining}
end
def extract_from_script(<<@depth::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.Depth{}, remaining}
end
def extract_from_script(<<@drop::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.Drop{}, remaining}
end
def extract_from_script(<<@dup::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.Dup{}, remaining}
end
def extract_from_script(<<@rot::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.Rot{}, remaining}
end
def extract_from_script(<<@swap::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.Swap{}, remaining}
end
def extract_from_script(<<@two_dup::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.TwoDup{}, remaining}
end
def extract_from_script(<<@two_over::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.TwoOver{}, remaining}
end
def extract_from_script(<<@two_swap::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Stack.TwoSwap{}, remaining}
end
def extract_from_script(<<@size::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Splice.Size{}, remaining}
end
def extract_from_script(<<@equal::8, remaining::bitstring>>, _whole_script) do
{:opcode, %BitwiseLogic.Equal{}, remaining}
end
def extract_from_script(<<@equal_verify::8, remaining::bitstring>>, _whole_script) do
{:opcode, %BitwiseLogic.EqualVerify{}, remaining}
end
def extract_from_script(<<@one_add::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.OneAdd{}, remaining}
end
def extract_from_script(<<@one_sub::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.OneSub{}, remaining}
end
def extract_from_script(<<@sub::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Sub{}, remaining}
end
def extract_from_script(<<@bool_or::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.BoolOr{}, remaining}
end
def extract_from_script(<<@less_than::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.LessThan{}, remaining}
end
def extract_from_script(<<@greater_than::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.GreaterThan{}, remaining}
end
def extract_from_script(<<@negate::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Negate{}, remaining}
end
def extract_from_script(<<@abs::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Abs{}, remaining}
end
def extract_from_script(<<@not_::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Not{}, remaining}
end
def extract_from_script(<<@min::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Min{}, remaining}
end
def extract_from_script(<<@within::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Arithmetic.Within{}, remaining}
end
def extract_from_script(<<@sha1::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.Sha1{}, remaining}
end
def extract_from_script(<<@sha256::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.Sha256{}, remaining}
end
def extract_from_script(<<@ripemd160::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.Ripemd160{}, remaining}
end
def extract_from_script(<<@hash160::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.Hash160{}, remaining}
end
def extract_from_script(<<@hash256::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.Hash256{}, remaining}
end
def extract_from_script(<<@code_separator::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.CodeSeparator{}, remaining}
end
def extract_from_script(<<@check_sig::8, remaining::bitstring>>, whole_script) do
{:opcode, %Crypto.CheckSig{script: whole_script}, remaining}
end
def extract_from_script(<<@check_sig_verify::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.CheckSigVerify{}, remaining}
end
def extract_from_script(<<@check_multi_sig::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.CheckMultiSig{}, remaining}
end
def extract_from_script(<<@check_multi_sig_verify::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Crypto.CheckMultiSigVerify{}, remaining}
end
def extract_from_script(<<@nop1::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Reserved.Nop1{}, remaining}
end
def extract_from_script(<<@invalid_opcode::8, remaining::bitstring>>, _whole_script) do
{:opcode, %PseudoWords.InvalidOpcode{}, remaining}
end
def extract_from_script(<<@check_lock_time_verify::8, remaining::bitstring>>, _whole_script) do
{:opcode, %Locktime.CheckLockTimeVerify{}, remaining}
end
def extract_from_script(<<unknown_opcode::8, remaining::bitstring>> = script, _whole_script) do
case unknown_opcode do
opcode when opcode in 0x01..0x4B -> extract_and_return_data(script)
_ -> {:error, "trying to extract an unknown opcode: #{unknown_opcode}", remaining}
end
end
defp extract_and_return_data(script) do
%CompactInteger{value: data_length, remaining: remaining} =
CompactInteger.extract_from(script)
remaining_size = byte_size(remaining)
if data_length <= remaining_size do
bits_data_length = data_length * @byte
<<data::bitstring-size(bits_data_length), remaining::bitstring>> = remaining
{:data, data, remaining}
else
{
:error,
"trying to get #{data_length} bytes out of a #{remaining_size} bytes binary",
remaining
}
end
end
end