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

Add references to transactions, plan and previous state in block header #30

Merged
merged 9 commits into from
Oct 2, 2019
123 changes: 89 additions & 34 deletions block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,15 @@ func (kind Kind) String() string {
// A Header defines properties of a Block that are not application-specific.
// These properties are required by, or produced by, the consensus algorithm.
type Header struct {
kind Kind // Kind of Block
parentHash id.Hash // Hash of the Block parent
baseHash id.Hash // Hash of the Block base
height Height // Height at which the Block was committed
round Round // Round at which the Block was committed
timestamp Timestamp // Seconds since Unix Epoch
kind Kind // Kind of Block
parentHash id.Hash // Hash of the Block parent
baseHash id.Hash // Hash of the Block base
txsRef id.Hash // Reference to the Block txs
planRef id.Hash // Reference to the Block plan
prevStateRef id.Hash // Reference to the Block previous state
height Height // Height at which the Block was committed
round Round // Round at which the Block was committed
timestamp Timestamp // Seconds since Unix Epoch

// Signatories oversee the consensus algorithm (must be nil unless the Block
// is a Rebase/Base Block)
Expand All @@ -61,7 +64,7 @@ type Header struct {

// NewHeader returns a Header. It will panic if a pre-condition for Header
// validity is violated.
func NewHeader(kind Kind, parentHash, baseHash id.Hash, height Height, round Round, timestamp Timestamp, signatories id.Signatories) Header {
func NewHeader(kind Kind, parentHash, baseHash, txsRef, planRef, prevStateRef id.Hash, height Height, round Round, timestamp Timestamp, signatories id.Signatories) Header {
switch kind {
case Standard:
if signatories != nil {
Expand Down Expand Up @@ -90,13 +93,16 @@ func NewHeader(kind Kind, parentHash, baseHash id.Hash, height Height, round Rou
panic("pre-condition violation: timestamp has not passed")
}
return Header{
kind: kind,
parentHash: parentHash,
baseHash: baseHash,
height: height,
round: round,
timestamp: timestamp,
signatories: signatories,
kind: kind,
parentHash: parentHash,
baseHash: baseHash,
txsRef: txsRef,
planRef: planRef,
prevStateRef: prevStateRef,
height: height,
round: round,
timestamp: timestamp,
signatories: signatories,
}
}

Expand All @@ -115,6 +121,21 @@ func (header Header) BaseHash() id.Hash {
return header.baseHash
}

// TxsRef of the Block.
func (header Header) TxsRef() id.Hash {
return header.txsRef
}

// PlanRef of the Block.
func (header Header) PlanRef() id.Hash {
return header.planRef
}

// PrevStateRef of the Block.
func (header Header) PrevStateRef() id.Hash {
return header.prevStateRef
}

// Height of the Block.
func (header Header) Height() Height {
return header.height
Expand All @@ -138,29 +159,56 @@ func (header Header) Signatories() id.Signatories {
// String implements the `fmt.Stringer` interface for the Header type.
func (header Header) String() string {
return fmt.Sprintf(
"Header(Kind=%v,ParentHash=%v,BaseHash=%v,Height=%v,Round=%v,Timestamp=%v,Signatories=%v)",
"Header(Kind=%v,ParentHash=%v,BaseHash=%v,TxsRef=%v,PlanRef=%v,PrevStateRef=%v,Height=%v,Round=%v,Timestamp=%v,Signatories=%v)",
header.kind,
header.parentHash,
header.baseHash,
header.txsRef,
header.planRef,
header.prevStateRef,
header.height,
header.round,
header.timestamp,
header.signatories,
)
}

// Data stores application-specific information used in Blocks and Notes (must
// be nil in Rebase Blocks and Base Blocks).
type Data []byte
// Txs stores application-specific transaction data used in Blocks and Notes
// (must be nil in Rebase Blocks and Base Blocks).
type Txs []byte

// Hash of a Txs object.
func (txs Txs) Hash() id.Hash {
return sha256.Sum256(txs)
}

// String implements the `fmt.Stringer` interface for the Data type.
func (data Data) String() string {
return base64.RawStdEncoding.EncodeToString(data)
// String implements the `fmt.Stringer` interface for the Txs type.
func (txs Txs) String() string {
return base64.RawStdEncoding.EncodeToString(txs)
}

// Plan stores application-specific plan data used in Blocks and Notes (must be
// nil in Rebase Blocks and Base Blocks).
type Plan []byte

// Hash of a Plan object.
func (plan Plan) Hash() id.Hash {
return sha256.Sum256(plan)
}

// String implements the `fmt.Stringer` interface for the Plan type.
func (plan Plan) String() string {
return base64.RawStdEncoding.EncodeToString(plan)
}

// State stores application-specific state after the execution of a Block.
type State []byte

// Hash of a State object.
func (state State) Hash() id.Hash {
return sha256.Sum256(state)
}

// String implements the `fmt.Stringer` interface for the State type.
func (state State) String() string {
return base64.RawStdEncoding.EncodeToString(state)
Expand All @@ -173,19 +221,21 @@ type Blocks []Block
// guarantees a consistent ordering of Blocks that is agreed upon by all members
// in a distributed network, even when some of the members are malicious.
type Block struct {
hash id.Hash // Hash of the Header, Data, and State
hash id.Hash // Hash of the Header, Txs, Plan, and State
header Header
data Data
txs Txs
plan Plan
prevState State
}

// New Block with the Header, Data, and State of the Block parent. The Block
// Hash will automatically be computed and set.
func New(header Header, data Data, prevState State) Block {
// New Block with the Header, Txs, Plan, and State of the Block parent. The
// Block Hash will automatically be computed and set.
func New(header Header, txs Txs, plan Plan, prevState State) Block {
return Block{
hash: ComputeHash(header, data, prevState),
hash: ComputeHash(header, txs, plan, prevState),
header: header,
data: data,
txs: txs,
plan: plan,
prevState: prevState,
}
}
Expand All @@ -200,9 +250,14 @@ func (block Block) Header() Header {
return block.header
}

// Data embedded in the Block for application-specific purposes.
func (block Block) Data() Data {
return block.data
// Txs embedded in the Block for application-specific purposes.
func (block Block) Txs() Txs {
return block.txs
}

// Plan embedded in the Block for application-specific purposes.
func (block Block) Plan() Plan {
return block.plan
}

// PreviousState embedded in the Block for application-specific state after the
Expand All @@ -213,7 +268,7 @@ func (block Block) PreviousState() State {

// String implements the `fmt.Stringer` interface for the Block type.
func (block Block) String() string {
return fmt.Sprintf("Block(Hash=%v,Header=%v,Data=%v,PreviousState=%v)", block.hash, block.header, block.data, block.prevState)
return fmt.Sprintf("Block(Hash=%v,Header=%v,Txs=%vPlan=%v,PreviousState=%v)", block.hash, block.header, block.txs, block.plan, block.prevState)
jazg marked this conversation as resolved.
Show resolved Hide resolved
}

// Equal compares one Block with another by checking that their Hashes are the
Expand Down Expand Up @@ -242,6 +297,6 @@ var (
)

// ComputeHash of a block basing on its header, data and previous state.
func ComputeHash(header Header, data Data, prevState State) id.Hash {
return sha256.Sum256([]byte(fmt.Sprintf("BlockHash(Header=%v,Data=%v,PreviousState=%v)", header, data, prevState)))
func ComputeHash(header Header, txs Txs, plan Plan, prevState State) id.Hash {
return sha256.Sum256([]byte(fmt.Sprintf("BlockHash(Header=%v,Txs=%v,Plan=%v,PreviousState=%v)", header, txs, plan, prevState)))
}
70 changes: 50 additions & 20 deletions block/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ var _ = Describe("Block", func() {
})

Context("when initializing a new block header", func() {

Context("when the block header is well-formed", func() {
It("should return a block header with fields equal to those passed during creation", func() {
test := func() bool {
Expand All @@ -112,6 +111,9 @@ var _ = Describe("Block", func() {
Expect(header.Kind()).Should(Equal(headerInit.Kind))
Expect(header.ParentHash()).Should(Equal(headerInit.ParentHash))
Expect(header.BaseHash()).Should(Equal(headerInit.BaseHash))
Expect(header.TxsRef()).Should(Equal(headerInit.TxsRef))
Expect(header.PlanRef()).Should(Equal(headerInit.PlanRef))
Expect(header.PrevStateRef()).Should(Equal(headerInit.PrevStateRef))
Expect(header.Height()).Should(Equal(headerInit.Height))
Expect(header.Round()).Should(Equal(headerInit.Round))
Expect(header.Timestamp()).Should(Equal(headerInit.Timestamp))
Expand Down Expand Up @@ -281,35 +283,63 @@ var _ = Describe("Block", func() {
})
})

Context("Block Data", func() {
Context("when stringifying random block data", func() {
Context("when block data is equal", func() {
Context("Block txs", func() {
Context("when stringifying random block txs", func() {
Context("when block txs is equal", func() {
It("should return equal strings", func() {
test := func(data Data) bool {
dataCopy := make(Data, len(data))
copy(dataCopy, data)
test := func(txs Txs) bool {
txsCopy := make(Txs, len(txs))
copy(txsCopy, txs)

return data.String() == dataCopy.String()
return txs.String() == txsCopy.String()
}
Expect(quick.Check(test, nil)).Should(Succeed())
})
})

Context("when block data is unequal", func() {
Context("when block txs is unequal", func() {
It("should return unequal strings", func() {
test := func(data1, data2 Data) bool {
if bytes.Equal(data1, data2) {
test := func(txs1, txs2 Txs) bool {
if bytes.Equal(txs1, txs2) {
return true
}
return data1.String() != data2.String()
return txs1.String() != txs2.String()
}
Expect(quick.Check(test, nil)).Should(Succeed())
})
})
})
})

Context("Block State", func() {
Context("Block plan", func() {
Context("when stringifying random block plan", func() {
Context("when block plan is equal", func() {
It("should return equal strings", func() {
test := func(plan Plan) bool {
planCopy := make(Plan, len(plan))
copy(planCopy, plan)

return plan.String() == planCopy.String()
}
Expect(quick.Check(test, nil)).Should(Succeed())
})
})

Context("when block plan is unequal", func() {
It("should return unequal strings", func() {
test := func(plan1, plan2 Plan) bool {
if bytes.Equal(plan1, plan2) {
return true
}
return plan1.String() != plan2.String()
}
Expect(quick.Check(test, nil)).Should(Succeed())
})
})
})
})

Context("Block state", func() {
Context("when stringifying random block state", func() {
Context("when block state is equal", func() {
It("should return equal strings", func() {
Expand Down Expand Up @@ -338,7 +368,6 @@ var _ = Describe("Block", func() {
})

Context("Block", func() {

Context("when stringifying random blocks", func() {
Context("when blocks are equal", func() {
It("should return equal strings", func() {
Expand Down Expand Up @@ -399,14 +428,15 @@ var _ = Describe("Block", func() {
It("should return a block with fields equal to those passed during creation", func() {
test := func() bool {
header := RandomBlockHeader(RandomBlockKind())
data, state := RandomBytesSlice(), RandomBytesSlice()
txs, plan, state := RandomBytesSlice(), RandomBytesSlice(), RandomBytesSlice()

// Expect the block has a valid hash
block := New(header, data, state)
block := New(header, txs, plan, state)
Expect(block.Hash()).ShouldNot(Equal(InvalidHash))

Expect(block.Header().String()).Should(Equal(header.String()))
Expect(block.Data()).Should(Equal(Data(data)))
Expect(block.Txs()).Should(Equal(Txs(txs)))
Expect(block.Plan()).Should(Equal(Plan(plan)))
Expect(block.PreviousState()).Should(Equal(State(state)))

return true
Expand All @@ -418,10 +448,10 @@ var _ = Describe("Block", func() {
It("should return a block with computed hashes that are equal", func() {
test := func() bool {
header := RandomBlockHeader(RandomBlockKind())
data, state := RandomBytesSlice(), RandomBytesSlice()
txs, plan, state := RandomBytesSlice(), RandomBytesSlice(), RandomBytesSlice()

block1 := New(header, data, state)
block2 := New(header, data, state)
block1 := New(header, txs, plan, state)
block2 := New(header, txs, plan, state)

Expect(block1.Hash()).Should(Equal(block2.Hash()))
Expect(block1.Equal(block2)).Should(BeTrue())
Expand Down
Loading