-
Notifications
You must be signed in to change notification settings - Fork 19
smite-ir: add CreateFundingTransaction IR operation #90
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,6 +66,14 @@ pub enum Operation { | |
| /// Extract a field from a parsed `accept_channel` response. | ||
| /// Input: `AcceptChannel`. | ||
| ExtractAcceptChannel(AcceptChannelField), | ||
| /// Create a BOLT 3 funding transaction for the channel funding flow. | ||
| /// | ||
| /// Inputs (4): | ||
| /// 0: `opener_funding_pubkey` (`Point`) | ||
| /// 1: `acceptor_funding_pubkey` (`Point`) | ||
| /// 2: `funding_satoshis` (`Amount`) | ||
| /// 3: `feerate_per_kw` (`FeeratePerKw`) | ||
| CreateFundingTransaction, | ||
|
|
||
| // -- Build: construct a BOLT message from inputs -- | ||
| /// Build an `open_channel` message (BOLT 2, type 32). | ||
|
|
@@ -532,6 +540,7 @@ impl fmt::Display for Operation { | |
| // Operations with inputs: parens added by Program::Display. | ||
| Self::DerivePoint => write!(f, "DerivePoint"), | ||
| Self::ExtractAcceptChannel(field) => write!(f, "Extract{field}"), | ||
| Self::CreateFundingTransaction => write!(f, "CreateFundingTransaction"), | ||
| Self::BuildOpenChannel => write!(f, "BuildOpenChannel"), | ||
| Self::SendMessage => write!(f, "SendMessage"), | ||
| } | ||
|
|
@@ -557,6 +566,7 @@ impl Operation { | |
| Self::LoadTargetPubkeyFromContext | Self::DerivePoint => Some(VariableType::Point), | ||
| Self::LoadChainHashFromContext => Some(VariableType::ChainHash), | ||
| Self::ExtractAcceptChannel(field) => Some(field.output_type()), | ||
| Self::CreateFundingTransaction => Some(VariableType::FundingTransaction), | ||
| Self::BuildOpenChannel => Some(VariableType::Message), | ||
| Self::SendMessage | Self::MineBlocks(_) => None, | ||
| Self::RecvAcceptChannel => Some(VariableType::AcceptChannel), | ||
|
|
@@ -587,6 +597,12 @@ impl Operation { | |
| Self::DerivePoint => vec![VariableType::PrivateKey], | ||
| Self::ExtractAcceptChannel(_) => vec![VariableType::AcceptChannel], | ||
| Self::SendMessage => vec![VariableType::Message], | ||
| Self::CreateFundingTransaction => vec![ | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: the ordering is inconsistent in some places -- in general |
||
| VariableType::Point, // opener_funding_pubkey | ||
| VariableType::Point, // acceptor_funding_pubkey | ||
| VariableType::Amount, // funding_satoshis | ||
| VariableType::FeeratePerKw, // feerate_per_kw | ||
| ], | ||
|
|
||
| Self::BuildOpenChannel => vec![ | ||
| VariableType::ChainHash, // chain_hash | ||
|
|
@@ -639,7 +655,8 @@ impl Operation { | |
| | Self::ExtractAcceptChannel(_) | ||
| | Self::BuildOpenChannel | ||
| | Self::SendMessage | ||
| | Self::MineBlocks(_) => vec![], | ||
| | Self::MineBlocks(_) | ||
| | Self::CreateFundingTransaction => vec![], | ||
|
|
||
| Self::RecvAcceptChannel => AcceptChannelField::ALL | ||
| .iter() | ||
|
|
@@ -673,7 +690,8 @@ impl Operation { | |
| | Self::DerivePoint | ||
| | Self::BuildOpenChannel | ||
| | Self::SendMessage | ||
| | Self::RecvAcceptChannel => false, | ||
| | Self::RecvAcceptChannel | ||
| | Self::CreateFundingTransaction => false, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -234,6 +234,22 @@ fn postcard_roundtrip() { | |
| operation: Operation::MineBlocks(6), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadPrivateKey(key(2)), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::DerivePoint, | ||
| inputs: vec![7], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadFeeratePerKw(15_000), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::CreateFundingTransaction, | ||
| inputs: vec![1, 8, 4, 9], | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
|
|
@@ -298,6 +314,184 @@ fn validate_rejects_mine_blocks_with_wrong_input() { | |
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn create_funding_transaction_operation() { | ||
| let op = Operation::CreateFundingTransaction; | ||
| assert_eq!( | ||
| op.input_types(), | ||
| vec![ | ||
| VariableType::Point, | ||
| VariableType::Point, | ||
| VariableType::Amount, | ||
| VariableType::FeeratePerKw, | ||
| ], | ||
| ); | ||
| assert_eq!(op.output_type(), Some(VariableType::FundingTransaction)); | ||
| assert!(!op.is_param_mutable()); | ||
| } | ||
|
|
||
| fn create_funding_transaction_instructions() -> Vec<Instruction> { | ||
| vec![ | ||
| Instruction { | ||
| operation: Operation::LoadPrivateKey(key(1)), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::DerivePoint, | ||
| inputs: vec![0], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadPrivateKey(key(2)), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::DerivePoint, | ||
| inputs: vec![2], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadAmount(10_000_000), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadFeeratePerKw(15_000), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::CreateFundingTransaction, | ||
| inputs: vec![1, 3, 4, 5], | ||
| }, | ||
| ] | ||
| } | ||
|
|
||
| #[test] | ||
| fn displays_create_funding_transaction_program() { | ||
| let program = Program { | ||
| instructions: create_funding_transaction_instructions(), | ||
| }; | ||
| let text = program.to_string(); | ||
| let lines: Vec<&str> = text.lines().collect(); | ||
|
|
||
| let z31 = "00".repeat(31); | ||
|
|
||
| let expected = vec![ | ||
| format!("v0 = LoadPrivateKey(0x{z31}01)"), | ||
| "v1 = DerivePoint(v0)".into(), | ||
| format!("v2 = LoadPrivateKey(0x{z31}02)"), | ||
| "v3 = DerivePoint(v2)".into(), | ||
| "v4 = LoadAmount(10000000)".into(), | ||
| "v5 = LoadFeeratePerKw(15000)".into(), | ||
| "v6 = CreateFundingTransaction(v1, v3, v4, v5)".into(), | ||
| ]; | ||
| assert_eq!(lines, expected); | ||
| } | ||
|
|
||
| #[test] | ||
| fn validate_accepts_create_funding_transaction() { | ||
| let program = Program { | ||
| instructions: create_funding_transaction_instructions(), | ||
| }; | ||
| program | ||
| .validate() | ||
| .expect("CreateFundingTransaction should validate"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn validate_rejects_create_funding_transaction_with_wrong_input_count() { | ||
| let program = Program { | ||
| instructions: vec![Instruction { | ||
| operation: Operation::CreateFundingTransaction, | ||
| inputs: vec![], | ||
| }], | ||
| }; | ||
| assert_eq!( | ||
| program.validate(), | ||
| Err(ValidateError::WrongInputCount { | ||
| instr: 0, | ||
| expected: 4, | ||
| got: 0, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn validate_rejects_create_funding_transaction_with_wrong_input_type() { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this test is hard to follow. I think it would be easier to understand if it were split into 4 tests with a helper function |
||
| fn program_with_wrong_input(pos: usize, wrong: Operation) -> Program { | ||
| let mut inputs = vec![2, 2, 3, 4]; | ||
| inputs[pos] = 0; | ||
|
|
||
| Program { | ||
| instructions: vec![ | ||
| Instruction { | ||
| operation: wrong, // wrong-typed value | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadPrivateKey(key(1)), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::DerivePoint, | ||
| inputs: vec![1], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadAmount(100_000), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::LoadFeeratePerKw(1_500), | ||
| inputs: vec![], | ||
| }, | ||
| Instruction { | ||
| operation: Operation::CreateFundingTransaction, | ||
| inputs, | ||
| }, | ||
| ], | ||
| } | ||
| } | ||
|
|
||
| // input pos, wrong, expected, got | ||
| let cases = [ | ||
| ( | ||
| 0, | ||
| Operation::LoadAmount(100_000), | ||
| VariableType::Point, | ||
| VariableType::Amount, | ||
| ), | ||
| ( | ||
| 1, | ||
| Operation::LoadFeeratePerKw(1_500), | ||
| VariableType::Point, | ||
| VariableType::FeeratePerKw, | ||
| ), | ||
| ( | ||
| 2, | ||
| Operation::LoadPrivateKey(key(1)), | ||
| VariableType::Amount, | ||
| VariableType::PrivateKey, | ||
| ), | ||
| ( | ||
| 3, | ||
| Operation::LoadAmount(100_000), | ||
| VariableType::FeeratePerKw, | ||
| VariableType::Amount, | ||
| ), | ||
| ]; | ||
|
|
||
| for (input, wrong, expected, got) in cases { | ||
| let result = program_with_wrong_input(input, wrong).validate(); | ||
|
|
||
| assert_eq!( | ||
| result, | ||
| Err(ValidateError::TypeMismatch { | ||
| instr: 5, | ||
| input, | ||
| expected, | ||
| got, | ||
| }), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Ensure AcceptChannelField and AcceptChannelField::ALL stay in sync. The | ||
| // exhaustive match in this test will fail to compile if a variant is added | ||
| // without updating it, and the assertion will fail if the match is updated | ||
|
|
@@ -685,6 +879,14 @@ fn generate_fresh_accept_channel_panics() { | |
| builder.generate_fresh(VariableType::AcceptChannel, &mut rng); | ||
| } | ||
|
|
||
| #[test] | ||
| #[should_panic(expected = "cannot generate fresh FundingTransaction")] | ||
| fn generate_fresh_funding_transaction_panics() { | ||
| let mut rng = SmallRng::seed_from_u64(0); | ||
| let mut builder = ProgramBuilder::new(); | ||
| builder.generate_fresh(VariableType::FundingTransaction, &mut rng); | ||
| } | ||
|
|
||
| #[test] | ||
| #[should_panic(expected = "expected 1 inputs, got 0")] | ||
| fn append_wrong_input_count_panics() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: should we call it
BuildFundingTransactionfor consistency with similar operations?