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}) <> end def encode_opcode(%Constants.PushData2{value: value}) do opcode = Constants.PushData2.encode() data = Data.encode(%Data{value: value}) <> end def encode_opcode(%Constants.PushData4{value: value}) do opcode = Constants.PushData4.encode() data = Data.encode(%Data{value: value}) <> 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 <> = remaining remaining_size = byte_size(remaining) if data_byte_size <= remaining_size do <> = 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 <> = remaining remaining_size = byte_size(remaining) if data_byte_size <= remaining_size do <> = 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 <> = remaining remaining_size = byte_size(remaining) if data_byte_size <= remaining_size do <> = 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(<> = 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 <> = remaining {:data, data, remaining} else { :error, "trying to get #{data_length} bytes out of a #{remaining_size} bytes binary", remaining } end end end