forked from lightninglabs/pool
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request lightninglabs#37 from lightninglabs/master-acct-si…
…gning account: introduce new account.Auctioneer struct for the master account output
- Loading branch information
Showing
8 changed files
with
442 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package account | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/btcsuite/btcutil" | ||
"github.com/lightninglabs/loop/lndclient" | ||
"github.com/lightningnetwork/lnd/input" | ||
"github.com/lightningnetwork/lnd/keychain" | ||
) | ||
|
||
// Auctioneer is the master auctioneer account, this will be threaded along in | ||
// the batch along side all the other accounts. We'll use this account to | ||
// accrue all the execution fees we earn each batch. | ||
type Auctioneer struct { | ||
// OutPoint is the outpoint of the master account. If this is a zero | ||
// outpoint, then no account exists yet. | ||
OutPoint wire.OutPoint | ||
|
||
// Balance is the current balance of the master account. | ||
// | ||
// TODO(roasbeef): need to account for external sends? to replenish? | ||
Balance btcutil.Amount | ||
|
||
// AuctioneerKey is the base key for the auctioneer, this is a static | ||
// parameter that's created when the system is initialized. | ||
AuctioneerKey *keychain.KeyDescriptor | ||
|
||
// BatchKey is the current batch key for the auctioneer's account, this | ||
// will be incremented by one each batch. | ||
BatchKey [33]byte | ||
} | ||
|
||
// AccountWitnessScript computes the raw witness script of the target account. | ||
func (a *Auctioneer) AccountWitnessScript() ([]byte, error) { | ||
batchKey, err := btcec.ParsePubKey( | ||
a.BatchKey[:], btcec.S256(), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return AuctioneerWitnessScript( | ||
batchKey, a.AuctioneerKey.PubKey, | ||
) | ||
} | ||
|
||
// Output returns the output of auctioneer's output as it should appear on the | ||
// last batch transaction. | ||
func (a *Auctioneer) Output() (*wire.TxOut, error) { | ||
witnessScript, err := a.AccountWitnessScript() | ||
if err != nil { | ||
return nil, err | ||
} | ||
pkScript, err := input.WitnessScriptHash(witnessScript) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &wire.TxOut{ | ||
PkScript: pkScript, | ||
Value: int64(a.Balance), | ||
}, nil | ||
} | ||
|
||
// AccountWitness attempts to construct a fully valid witness which can be used | ||
// to spend the auctioneer's account on the batch execution transaction. | ||
func (a *Auctioneer) AccountWitness(signer lndclient.SignerClient, | ||
tx *wire.MsgTx, inputIndex int) (wire.TxWitness, error) { | ||
|
||
// First, we'll compute the witness script, and its corresponding | ||
// witness program as we'll need both for the sign descriptor below. | ||
witnessScript, err := a.AccountWitnessScript() | ||
if err != nil { | ||
return nil, err | ||
} | ||
pkScript, err := input.WitnessScriptHash(witnessScript) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Next, as the raw auctioneer key isn't used in the output scripts, | ||
// we'll construct the current account tweak using the current batch | ||
// key: | ||
// * batchTweak = sha256(batchKey || auctioneerKey) | ||
// * accountKey = auctioneerKey + batchTweak*G | ||
batchKey, err := btcec.ParsePubKey( | ||
a.BatchKey[:], btcec.S256(), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
batchTweak := input.SingleTweakBytes( | ||
batchKey, a.AuctioneerKey.PubKey, | ||
) | ||
|
||
// Now that we have all the required items, we'll query the Signer for | ||
// a valid signature for our account output. | ||
signDesc := &input.SignDescriptor{ | ||
KeyDesc: *a.AuctioneerKey, | ||
SingleTweak: batchTweak, | ||
WitnessScript: witnessScript, | ||
Output: &wire.TxOut{ | ||
Value: int64(a.Balance), | ||
PkScript: pkScript, | ||
}, | ||
HashType: txscript.SigHashAll, | ||
InputIndex: inputIndex, | ||
SigHashes: txscript.NewTxSigHashes(tx), | ||
} | ||
ctx := context.Background() | ||
sigs, err := signer.SignOutputRaw( | ||
ctx, tx, []*input.SignDescriptor{signDesc}, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Finally we'll construct the account witness given our witness | ||
// script, pubkey, and signature. | ||
return AuctioneerWitness(witnessScript, sigs[0]), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package account | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/lightningnetwork/lnd/input" | ||
"github.com/lightningnetwork/lnd/keychain" | ||
) | ||
|
||
type mockSigner struct { | ||
privKey *btcec.PrivateKey | ||
} | ||
|
||
func (m *mockSigner) SignOutputRaw(ctx context.Context, tx *wire.MsgTx, | ||
signDescriptors []*input.SignDescriptor) ([][]byte, error) { | ||
|
||
s := input.MockSigner{ | ||
Privkeys: []*btcec.PrivateKey{ | ||
m.privKey, | ||
}, | ||
} | ||
|
||
sig, err := s.SignOutputRaw(tx, signDescriptors[0]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return [][]byte{sig}, nil | ||
} | ||
|
||
func (m *mockSigner) ComputeInputScript(ctx context.Context, tx *wire.MsgTx, | ||
signDescriptors []*input.SignDescriptor) ([]*input.Script, error) { | ||
return nil, nil | ||
} | ||
func (m *mockSigner) SignMessage(ctx context.Context, msg []byte, | ||
locator keychain.KeyLocator) ([]byte, error) { | ||
return nil, nil | ||
} | ||
func (m *mockSigner) VerifyMessage(ctx context.Context, msg, sig []byte, pubkey [33]byte) ( | ||
bool, error) { | ||
return false, nil | ||
} | ||
func (m *mockSigner) DeriveSharedKey(ctx context.Context, ephemeralPubKey *btcec.PublicKey, | ||
keyLocator *keychain.KeyLocator) ([32]byte, error) { | ||
return [32]byte{}, nil | ||
} | ||
|
||
// TestAuctioneerAccountWitness tests that we're able to properly produce a | ||
// valid witness for the auctioneer's account given a set of starting params. | ||
func TestAuctioneerAccountWitness(t *testing.T) { | ||
t.Parallel() | ||
|
||
// First we'll generate the auctioneer key and a random batch key that | ||
// we'll use for this purpose. | ||
batchKey, err := btcec.NewPrivateKey(btcec.S256()) | ||
if err != nil { | ||
t.Fatalf("unable to make batch key: %v", err) | ||
} | ||
auctioneerKey, err := btcec.NewPrivateKey(btcec.S256()) | ||
if err != nil { | ||
t.Fatalf("unable to make auctioneer key: %v", err) | ||
} | ||
|
||
// With these two keys generated, we can now make the full auctioneer | ||
// account. | ||
acct := &Auctioneer{ | ||
Balance: 1_000_000, | ||
AuctioneerKey: &keychain.KeyDescriptor{ | ||
PubKey: auctioneerKey.PubKey(), | ||
}, | ||
} | ||
copy(acct.BatchKey[:], batchKey.PubKey().SerializeCompressed()) | ||
|
||
acctOutput, err := acct.Output() | ||
if err != nil { | ||
t.Fatalf("unable to create acct output: %v", err) | ||
} | ||
|
||
// We'll construct a sweep transaction that just sweeps the output back | ||
// into an identical one. | ||
spendTx := wire.NewMsgTx(2) | ||
spendTx.AddTxIn(&wire.TxIn{}) | ||
spendTx.AddTxOut(acctOutput) | ||
|
||
// Now we'll construct the witness to simulate a spend of the account. | ||
signer := &mockSigner{auctioneerKey} | ||
witness, err := acct.AccountWitness(signer, spendTx, 0) | ||
if err != nil { | ||
t.Fatalf("unable to generate witness: %v", err) | ||
} | ||
spendTx.TxIn[0].Witness = witness | ||
|
||
// Ensure that the witness script we generate is valid. | ||
vm, err := txscript.NewEngine( | ||
acctOutput.PkScript, | ||
spendTx, 0, txscript.StandardVerifyFlags, nil, | ||
nil, acctOutput.Value, | ||
) | ||
if err != nil { | ||
t.Fatalf("unable to create engine: %v", err) | ||
} | ||
if err := vm.Execute(); err != nil { | ||
t.Fatalf("invalid spend: %v", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.