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

Pluralized Transaction Inputs #127

Merged
merged 25 commits into from
Aug 26, 2017

Conversation

chadlagore
Copy link
Contributor

@chadlagore chadlagore commented Aug 13, 2017

Status: Ready for review

Related Issue

126

Description

  • Transactions should reference multiple inputs.
  • This allows users to coalesce micro-payments into larger ones.

TODOs

  • Pluralize transactions
  • Get tests passing again.
  • Rebase brunos branch.

@chadlagore chadlagore changed the title Pluralized Transaction Inputs [WIP] Pluralized Transaction Inputs Aug 13, 2017
Copy link
Member

@jordanschalm jordanschalm left a comment

Choose a reason for hiding this comment

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

Looking good so far 👍

@@ -80,3 +90,25 @@ func (bc *BlockChain) CopyBlockByIndex(i uint32) (*Block, error) {
}
return nil, errors.New("block request out of bounds")
}

// InputsSpentElsewhere returns true if inputs perported to be only spent
Copy link
Member

Choose a reason for hiding this comment

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

purported

// to a transaction.
func (bc *BlockChain) GetBlockRange(t *Transaction) (uint32, uint32) {
min := uint32(math.MaxUint32)
max := uint32(math.MaxUint32) // Why I have to cast this? No idea.
Copy link
Member

Choose a reason for hiding this comment

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

should be 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Meant to use MinUint32 - nice catch!

@@ -223,9 +222,8 @@ func VerifyBlock(bc *blockchain.BlockChain,
// Check for multiple transactions referencing same input transaction.
for i, trA := range b.Transactions {
Copy link
Member

Choose a reason for hiding this comment

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

What about:

create input map M
for t in transactions:
  for inp in t.inputs:
    attempt to add inp to M
    if already in M: fail

@@ -53,7 +53,8 @@ func (p *Pool) GetN(N int) *blockchain.Transaction {

// GetIndex returns the index of the transaction in the ordering.
func (p *Pool) GetIndex(t *blockchain.Transaction) int {
Copy link
Member

Choose a reason for hiding this comment

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

We lose the property that attempting to add a transaction with an input that already exists will fail. Now we can do [IN1, IN2]->OUT1 and [IN1, IN3]->OUT2 and addition will succeed. What are we doing to mediate this and ensure we don't add to the pool if an input already exists? A many-to-one mapping for inputs -> transactions would be nice. Like IN1->OUT1, IN2->OUT2.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its actually ok that multiple transactions in the pool exist with the same input. A single transaction may have awarded 2 coins to alice and 1 to bob, that transaction will serve as an input in at most 2 transactions in the future.

I'll admit that this implementation isn't quite perfect though 😬.

Copy link
Contributor Author

@chadlagore chadlagore Aug 25, 2017

Choose a reason for hiding this comment

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

The same situation goes for testing double spends within blocks. Its ok that an input is referenced more than once, but not if it is referenced more than once by the same sender. Thanks to your map idea, we now do that check in linear time though 🎉

@chadlagore chadlagore changed the title [WIP] Pluralized Transaction Inputs Pluralized Transaction Inputs Aug 26, 2017
app/app.go Outdated
@@ -351,7 +351,7 @@ func (a *App) RunMiner() {
miningResult := miner.Mine(a.Chain, blockToMine)

if miningResult.Complete {
log.Info("Successfully mined a block!")
// log.Debug("Successfully mined a block!")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Quite noisy in tests!

Copy link
Member

Choose a reason for hiding this comment

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

We can just set logging levels in individual tests or in a TestMain.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.6%) to 76.16% when pulling a605059 on 126-allow-multiple-inputs-in-transactions into 836f6e7 on dev.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.6%) to 76.16% when pulling d246cb0 on 126-allow-multiple-inputs-in-transactions into 836f6e7 on dev.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.6%) to 76.16% when pulling fe4a0d6 on 126-allow-multiple-inputs-in-transactions into 836f6e7 on dev.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.5%) to 76.243% when pulling e24d342 on 126-allow-multiple-inputs-in-transactions into 836f6e7 on dev.

Copy link
Member

@bfbachmann bfbachmann left a comment

Choose a reason for hiding this comment

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

Extremely high quality 🏅

app/app.go Outdated
@@ -351,7 +351,7 @@ func (a *App) RunMiner() {
miningResult := miner.Mine(a.Chain, blockToMine)

if miningResult.Complete {
log.Info("Successfully mined a block!")
// log.Debug("Successfully mined a block!")
Copy link
Member

Choose a reason for hiding this comment

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

We can just set logging levels in individual tests or in a TestMain.

app/app_test.go Outdated
@@ -47,7 +47,7 @@ func TestPushHandlerNewTestTransaction(t *testing.T) {
select {
case tr, ok := <-a.transactionQueue:
assert.True(t, ok)
assert.Equal(t, tr, txn)
assert.ObjectsAreEqual(tr, txn)
Copy link
Member

Choose a reason for hiding this comment

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

Have to use Equal :(

assert.Equal(t, len(a.Chain.Blocks), 2)
a.blockQueue <- lastBlock
time.Sleep(time.Millisecond * 50)
assert.Equal(t, len(a.Chain.Blocks), 2)
Copy link
Member

Choose a reason for hiding this comment

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

Nice tests!

@@ -69,7 +69,7 @@ func (b *Block) Len() int {
}

// Marshal converts a Block to a byte slice.
func (b *Block) Marshal() []byte {
func (b Block) Marshal() []byte {
Copy link
Member

Choose a reason for hiding this comment

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

I actually think it's better to use the pointer receiver here because that way we don't have to copy the block when we call this.

// GetAllInputs returns all the transactions referenced by a transaction
// as inputs. Returns an error if any of the transactios requested could
// not be found.
func (bc *BlockChain) GetAllInputs(t *Transaction) ([]*Transaction, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Lovely

tA, _ := TxBody{
Sender: sender.Public(),
Inputs: []TxHashPointer{},
Outputs: []TxOutput{
Copy link
Member

Choose a reason for hiding this comment

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

It might be a good idea to enforce the condition that at least one of the outputs from transaction goes to someone other than the sender.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll sleep on this 😴

}

// InputsIntersect returns true if the inputs of t intersect with those of other.
func (t *Transaction) InputsIntersect(other *Transaction) bool {
Copy link
Member

Choose a reason for hiding this comment

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

❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️

t.TxBody.Input.Hash != blockchain.NilHash ||
t.Input.Index != 0 {
// Check that the input is 0 (only one input to CB).
input := t.TxBody.Inputs[0]
Copy link
Member

Choose a reason for hiding this comment

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

Might want to guard against an out-of-bounds error here.

ix := b.BlockNumber - 1
if int(ix) > len(bc.Blocks)-1 || ix < 0 {
lastBlockIdx := b.BlockNumber - 1
if int(lastBlockIdx) != len(bc.Blocks)-1 {
Copy link
Member

Choose a reason for hiding this comment

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

I would change the comment on this function to say something about the fact that we are making sure that this block can be added to the end of the chain.

miner/miner.go Outdated
@@ -120,8 +120,8 @@ func CloudBase(
// Increment the input index of every transaction that has an input in the
// new block
for _, tx := range b.Transactions[1:] {
Copy link
Member

Choose a reason for hiding this comment

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

I think this for loop should be removed because a transaction can't reference an input in the same block.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.4%) to 75.965% when pulling 4587504 on 126-allow-multiple-inputs-in-transactions into 6dc8be0 on dev.

@chadlagore chadlagore merged commit 27747c5 into dev Aug 26, 2017
@jordanschalm jordanschalm deleted the 126-allow-multiple-inputs-in-transactions branch August 30, 2017 21:10
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