diff --git a/common/adminBlock/EntryAddAuditServer.go b/common/adminBlock/EntryAddAuditServer.go index 9f00f6f7ac..71bb047cbf 100644 --- a/common/adminBlock/EntryAddAuditServer.go +++ b/common/adminBlock/EntryAddAuditServer.go @@ -1,7 +1,6 @@ package adminBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/constants" @@ -59,45 +58,52 @@ func (e *AddAuditServer) Type() byte { return constants.TYPE_ADD_AUDIT_SERVER } -func (e *AddAuditServer) MarshalBinary() (data []byte, err error) { +func (e *AddAuditServer) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } - data, err = e.IdentityChainID.MarshalBinary() + err = buf.PushBinaryMarshallable(e.IdentityChainID) if err != nil { return nil, err } - buf.Write(data) - binary.Write(&buf, binary.BigEndian, e.DBHeight) + err = buf.PushUInt32(e.DBHeight) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (e *AddAuditServer) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Add Federated Server Entry: %v", r) - } - }() +func (e *AddAuditServer) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + + b, err := buf.PopByte() + if err != nil { + return nil, err + } - newData = data - if newData[0] != e.Type() { + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - panic(err.Error()) + return nil, err } - e.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + e.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err + } - return + return buf.DeepCopyBytes(), nil } func (e *AddAuditServer) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryAddFederatedServer.go b/common/adminBlock/EntryAddFederatedServer.go index ee64ade67f..4aa6b75973 100644 --- a/common/adminBlock/EntryAddFederatedServer.go +++ b/common/adminBlock/EntryAddFederatedServer.go @@ -1,7 +1,6 @@ package adminBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/constants" @@ -60,45 +59,44 @@ func (e *AddFederatedServer) Type() byte { return constants.TYPE_ADD_FED_SERVER } -func (e *AddFederatedServer) MarshalBinary() (data []byte, err error) { +func (e *AddFederatedServer) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - - data, err = e.IdentityChainID.MarshalBinary() + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushUInt32(e.DBHeight) if err != nil { return nil, err } - buf.Write(data) - - binary.Write(&buf, binary.BigEndian, e.DBHeight) return buf.DeepCopyBytes(), nil } -func (e *AddFederatedServer) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Add Federated Server Entry: %v", r) - } - }() - - newData = data - if newData[0] != e.Type() { +func (e *AddFederatedServer) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - panic(err.Error()) + return nil, err + } + e.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err } - e.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - - return + return buf.DeepCopyBytes(), nil } func (e *AddFederatedServer) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryAddFederatedServerBitcoinAnchorKey.go b/common/adminBlock/EntryAddFederatedServerBitcoinAnchorKey.go index 40003a75cb..eaffc50b59 100644 --- a/common/adminBlock/EntryAddFederatedServerBitcoinAnchorKey.go +++ b/common/adminBlock/EntryAddFederatedServerBitcoinAnchorKey.go @@ -61,57 +61,60 @@ func (e *AddFederatedServerBitcoinAnchorKey) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - - data, err := e.IdentityChainID.MarshalBinary() + err := buf.PushByte(e.Type()) if err != nil { return nil, err } - buf.Write(data) - - buf.Write([]byte{e.KeyPriority}) - buf.Write([]byte{e.KeyType}) - data, err = e.ECDSAPublicKey.MarshalBinary() + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushByte(e.KeyPriority) + if err != nil { + return nil, err + } + err = buf.PushByte(e.KeyType) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(&e.ECDSAPublicKey) if err != nil { return nil, err } - buf.Write(data) return buf.DeepCopyBytes(), nil } -func (e *AddFederatedServerBitcoinAnchorKey) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Add Federated Server Bitcoin Anchor Key: %v", r) - } - }() - - newData = data - if newData[0] != e.Type() { +func (e *AddFederatedServerBitcoinAnchorKey) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - return + return nil, err + } + e.KeyPriority, err = buf.PopByte() + if err != nil { + return nil, err + } + e.KeyType, err = buf.PopByte() + if err != nil { + return nil, err } - - e.KeyPriority, newData = newData[0], newData[1:] - e.KeyType, newData = newData[0], newData[1:] if e.KeyType != 0 && e.KeyType != 1 { return nil, fmt.Errorf("Invalid KeyType") } - - newData, err = e.ECDSAPublicKey.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(&e.ECDSAPublicKey) if err != nil { - return + return nil, err } - return + return buf.DeepCopyBytes(), nil } func (e *AddFederatedServerBitcoinAnchorKey) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryAddFederatedServerSigningKey.go b/common/adminBlock/EntryAddFederatedServerSigningKey.go index e16714aa88..b076236ffe 100644 --- a/common/adminBlock/EntryAddFederatedServerSigningKey.go +++ b/common/adminBlock/EntryAddFederatedServerSigningKey.go @@ -1,7 +1,6 @@ package adminBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/constants" @@ -62,56 +61,53 @@ func (e *AddFederatedServerSigningKey) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - - data, err := e.IdentityChainID.MarshalBinary() + err := buf.PushByte(e.Type()) if err != nil { return nil, err } - buf.Write(data) - - buf.Write([]byte{e.KeyPriority}) - - data, err = e.PublicKey.MarshalBinary() + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushByte(e.KeyPriority) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(&e.PublicKey) + if err != nil { + return nil, err + } + err = buf.PushUInt32(e.DBHeight) if err != nil { return nil, err } - buf.Write(data) - - binary.Write(&buf, binary.BigEndian, e.DBHeight) return buf.DeepCopyBytes(), nil } -func (e *AddFederatedServerSigningKey) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Add Federated server Signing Key Entry: %v", r) - } - }() - - newData = data - if newData[0] != e.Type() { +func (e *AddFederatedServerSigningKey) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - return + return nil, err } - - e.KeyPriority, newData = newData[0], newData[1:] - - newData, err = e.PublicKey.UnmarshalBinaryData(newData) + e.KeyPriority, err = buf.PopByte() + err = buf.PopBinaryMarshallable(&e.PublicKey) + if err != nil { + return nil, err + } + e.DBHeight, err = buf.PopUInt32() if err != nil { - return + return nil, err } - e.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - - return + return buf.DeepCopyBytes(), nil } func (e *AddFederatedServerSigningKey) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryAddReplaceMatryoshkaHash.go b/common/adminBlock/EntryAddReplaceMatryoshkaHash.go index 0d750f6933..1f29601d02 100644 --- a/common/adminBlock/EntryAddReplaceMatryoshkaHash.go +++ b/common/adminBlock/EntryAddReplaceMatryoshkaHash.go @@ -53,41 +53,48 @@ func NewAddReplaceMatryoshkaHash(identityChainID interfaces.IHash, mHash interfa return e } -func (e *AddReplaceMatryoshkaHash) MarshalBinary() (data []byte, err error) { +func (e *AddReplaceMatryoshkaHash) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - buf.Write(e.IdentityChainID.Bytes()) - buf.Write(e.MHash.Bytes()) + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.MHash) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } func (e *AddReplaceMatryoshkaHash) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Add Replace Matryoshka Hash: %v", r) - } - }() - newData = data - if newData[0] != e.Type() { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if err != nil { + return nil, err + } + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - return + return nil, err } e.MHash = new(primitives.Hash) - newData, err = e.MHash.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.MHash) if err != nil { - return + return nil, err } - return + return buf.DeepCopyBytes(), nil } func (e *AddReplaceMatryoshkaHash) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryDBSignature.go b/common/adminBlock/EntryDBSignature.go index 529c7dc011..fa8a038c1e 100644 --- a/common/adminBlock/EntryDBSignature.go +++ b/common/adminBlock/EntryDBSignature.go @@ -57,24 +57,19 @@ func (e *DBSignatureEntry) Type() byte { return constants.TYPE_DB_SIGNATURE } -func (e *DBSignatureEntry) MarshalBinary() (data []byte, err error) { +func (e *DBSignatureEntry) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - - data, err = e.IdentityAdminChainID.MarshalBinary() + err := buf.PushByte(e.Type()) if err != nil { return nil, err } - buf.Write(data) - - _, err = buf.Write(e.PrevDBSig.GetPubBytes()) + err = buf.PushBinaryMarshallable(e.IdentityAdminChainID) if err != nil { return nil, err } - - _, err = buf.Write(e.PrevDBSig.GetSigBytes()) + err = buf.PushBinaryMarshallable(&e.PrevDBSig) if err != nil { return nil, err } @@ -82,27 +77,23 @@ func (e *DBSignatureEntry) MarshalBinary() (data []byte, err error) { return buf.DeepCopyBytes(), nil } -func (e *DBSignatureEntry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshallig DBSignature Entry: %v", r) - } - }() - newData = data - if newData[0] != e.Type() { +func (e *DBSignatureEntry) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] - e.IdentityAdminChainID = new(primitives.Hash) - newData, err = e.IdentityAdminChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityAdminChainID) if err != nil { - return + return nil, err + } + err = buf.PopBinaryMarshallable(&e.PrevDBSig) + if err != nil { + return nil, err } - newData, err = e.PrevDBSig.UnmarshalBinaryData(newData) - - return + return buf.DeepCopyBytes(), nil } func (e *DBSignatureEntry) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryEndOfMinute.go b/common/adminBlock/EntryEndOfMinute.go index a903fddcd7..4bacb89f3e 100644 --- a/common/adminBlock/EntryEndOfMinute.go +++ b/common/adminBlock/EntryEndOfMinute.go @@ -30,30 +30,37 @@ func NewEndOfMinuteEntry(minuteNumber byte) *EndOfMinuteEntry { return e } -func (e *EndOfMinuteEntry) MarshalBinary() (data []byte, err error) { +func (e *EndOfMinuteEntry) MarshalBinary() ([]byte, error) { var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - buf.Write([]byte{e.MinuteNumber}) + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushByte(e.MinuteNumber) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (e *EndOfMinuteEntry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling End Of Minute: %v", r) - } - }() - newData = data - if newData[0] != e.Type() { +func (e *EndOfMinuteEntry) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if err != nil { + return nil, err + } + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] - e.MinuteNumber, newData = newData[0], newData[1:] + e.MinuteNumber, err = buf.PopByte() + if err != nil { + return nil, err + } - return + return buf.DeepCopyBytes(), nil } func (e *EndOfMinuteEntry) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryIncreaseServerCount.go b/common/adminBlock/EntryIncreaseServerCount.go index 5b059ace5b..c5b5893b1d 100644 --- a/common/adminBlock/EntryIncreaseServerCount.go +++ b/common/adminBlock/EntryIncreaseServerCount.go @@ -31,31 +31,37 @@ func (e *IncreaseServerCount) Type() byte { return constants.TYPE_ADD_SERVER_COUNT } -func (e *IncreaseServerCount) MarshalBinary() (data []byte, err error) { +func (e *IncreaseServerCount) MarshalBinary() ([]byte, error) { var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - buf.Write([]byte{e.Amount}) + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushByte(e.Amount) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (e *IncreaseServerCount) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Entry Increase Server Count: %v", r) - } - }() - - newData = data - if newData[0] != e.Type() { +func (e *IncreaseServerCount) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if err != nil { + return nil, err + } + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] - e.Amount, newData = newData[0], newData[1:] + e.Amount, err = buf.PopByte() + if err != nil { + return nil, err + } - return + return buf.DeepCopyBytes(), nil } func (e *IncreaseServerCount) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryRemoveFederatedServer.go b/common/adminBlock/EntryRemoveFederatedServer.go index 7e43c9dc67..5721bee66e 100644 --- a/common/adminBlock/EntryRemoveFederatedServer.go +++ b/common/adminBlock/EntryRemoveFederatedServer.go @@ -1,7 +1,6 @@ package adminBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/constants" @@ -66,43 +65,44 @@ func (e *RemoveFederatedServer) Type() byte { return constants.TYPE_REMOVE_FED_SERVER } -func (e *RemoveFederatedServer) MarshalBinary() (data []byte, err error) { +func (e *RemoveFederatedServer) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - data, err = e.IdentityChainID.MarshalBinary() + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushUInt32(e.DBHeight) if err != nil { return nil, err } - buf.Write(data) - binary.Write(&buf, binary.BigEndian, e.DBHeight) return buf.DeepCopyBytes(), nil } -func (e *RemoveFederatedServer) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Remove Federated Server: %v", r) - } - }() - - newData = data - if newData[0] != e.Type() { +func (e *RemoveFederatedServer) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - return + return nil, err + } + e.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err } - e.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - - return + return buf.DeepCopyBytes(), nil } func (e *RemoveFederatedServer) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryRevealMatryoshkaHash.go b/common/adminBlock/EntryRevealMatryoshkaHash.go index 5e86fe06a1..f28a215d9c 100644 --- a/common/adminBlock/EntryRevealMatryoshkaHash.go +++ b/common/adminBlock/EntryRevealMatryoshkaHash.go @@ -42,41 +42,48 @@ func (c *RevealMatryoshkaHash) UpdateState(state interfaces.IState) error { return nil } -func (e *RevealMatryoshkaHash) MarshalBinary() (data []byte, err error) { +func (e *RevealMatryoshkaHash) MarshalBinary() ([]byte, error) { e.Init() var buf primitives.Buffer - buf.Write([]byte{e.Type()}) - buf.Write(e.IdentityChainID.Bytes()) - buf.Write(e.MHash.Bytes()) + err := buf.PushByte(e.Type()) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.IdentityChainID) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.MHash) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } func (e *RevealMatryoshkaHash) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Reveal Matryoshka Hash: %v", r) - } - }() - newData = data - if newData[0] != e.Type() { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if err != nil { + return nil, err + } + if b != e.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] e.IdentityChainID = new(primitives.Hash) - newData, err = e.IdentityChainID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.IdentityChainID) if err != nil { - return + return nil, err } e.MHash = new(primitives.Hash) - newData, err = e.MHash.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.MHash) if err != nil { - return + return nil, err } - return + return buf.DeepCopyBytes(), nil } func (e *RevealMatryoshkaHash) UnmarshalBinary(data []byte) (err error) { diff --git a/common/adminBlock/EntryServerFault.go b/common/adminBlock/EntryServerFault.go index 67ed31a0c3..cd61603c6f 100644 --- a/common/adminBlock/EntryServerFault.go +++ b/common/adminBlock/EntryServerFault.go @@ -1,7 +1,6 @@ package adminBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/constants" @@ -34,51 +33,57 @@ func (e *ServerFault) Init() { } } +var _ interfaces.IABEntry = (*ServerFault)(nil) +var _ interfaces.BinaryMarshallable = (*ServerFault)(nil) + type SigList struct { Length uint32 List []interfaces.IFullSignature } -var _ interfaces.IABEntry = (*ServerFault)(nil) -var _ interfaces.BinaryMarshallable = (*ServerFault)(nil) +var _ interfaces.BinaryMarshallable = (*SigList)(nil) func (sl *SigList) MarshalBinary() (data []byte, err error) { var buf primitives.Buffer - binary.Write(&buf, binary.BigEndian, uint32(sl.Length)) + buf.PushUInt32(sl.Length) for _, individualSig := range sl.List { if individualSig == nil { return nil, fmt.Errorf("Nil signature present") } - if d, err := individualSig.MarshalBinary(); err != nil { + err := buf.PushBinaryMarshallable(individualSig) + if err != nil { return nil, err - } else { - buf.Write(d) } } return buf.DeepCopyBytes(), nil } -func (sl *SigList) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling SigList in Full Server Fault: %v", r) - } - }() - newData = data - sl.Length, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] +func (sl *SigList) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) - for i := sl.Length; i > 0; i-- { + var err error + sl.Length, err = buf.PopUInt32() + if err != nil { + return nil, err + } + + for i := 0; i < int(sl.Length); i++ { tempSig := new(primitives.Signature) - newData, err = tempSig.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(tempSig) if err != nil { return nil, err } sl.List = append(sl.List, tempSig) } - return newData, nil + return buf.DeepCopyBytes(), nil +} + +func (m *SigList) UnmarshalBinary(data []byte) error { + _, err := m.UnmarshalBinaryData(data) + return err } func (e *ServerFault) UpdateState(state interfaces.IState) error { @@ -117,89 +122,91 @@ func (e *ServerFault) UpdateState(state interfaces.IState) error { return nil } -func (m *ServerFault) MarshalCore() (data []byte, err error) { +func (m *ServerFault) MarshalCore() ([]byte, error) { m.Init() - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error marshalling Server Fault Core: %v", r) - } - }() - var buf primitives.Buffer - if d, err := m.ServerID.MarshalBinary(); err != nil { + err := buf.PushBinaryMarshallable(m.ServerID) + if err != nil { return nil, err - } else { - buf.Write(d) } - if d, err := m.AuditServerID.MarshalBinary(); err != nil { + + err = buf.PushBinaryMarshallable(m.AuditServerID) + if err != nil { return nil, err - } else { - buf.Write(d) } - buf.WriteByte(m.VMIndex) - binary.Write(&buf, binary.BigEndian, uint32(m.DBHeight)) - binary.Write(&buf, binary.BigEndian, uint32(m.Height)) + err = buf.PushByte(m.VMIndex) + if err != nil { + return nil, err + } + err = buf.PushUInt32(m.DBHeight) + if err != nil { + return nil, err + } + err = buf.PushUInt32(m.Height) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (m *ServerFault) MarshalBinary() (data []byte, err error) { +func (m *ServerFault) MarshalBinary() ([]byte, error) { m.Init() - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error marshalling Invalid Server Fault: %v", r) - } - }() - var buf primitives.Buffer - buf.Write([]byte{m.Type()}) - if d, err := m.Timestamp.MarshalBinary(); err != nil { + err := buf.PushByte(m.Type()) + if err != nil { return nil, err - } else { - buf.Write(d) } - if d, err := m.ServerID.MarshalBinary(); err != nil { + err = buf.PushBinaryMarshallable(m.Timestamp) + if err != nil { return nil, err - } else { - buf.Write(d) } - if d, err := m.AuditServerID.MarshalBinary(); err != nil { + err = buf.PushBinaryMarshallable(m.ServerID) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(m.AuditServerID) + if err != nil { return nil, err - } else { - buf.Write(d) } - buf.WriteByte(m.VMIndex) - binary.Write(&buf, binary.BigEndian, uint32(m.DBHeight)) - binary.Write(&buf, binary.BigEndian, uint32(m.Height)) + err = buf.PushByte(m.VMIndex) + if err != nil { + return nil, err + } + err = buf.PushUInt32(m.DBHeight) + if err != nil { + return nil, err + } + err = buf.PushUInt32(m.Height) + if err != nil { + return nil, err + } - if d, err := m.SignatureList.MarshalBinary(); err != nil { + err = buf.PushBinaryMarshallable(&m.SignatureList) + if err != nil { return nil, err - } else { - buf.Write(d) } return buf.DeepCopyBytes(), nil } -func (m *ServerFault) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling With Signatures Invalid Server Fault: %v", r) - } - }() - newData = data - if newData[0] != m.Type() { +func (m *ServerFault) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + b, err := buf.PopByte() + if err != nil { + return nil, err + } + if b != m.Type() { return nil, fmt.Errorf("Invalid Entry type") } - newData = newData[1:] m.Timestamp = new(primitives.Timestamp) - newData, err = m.Timestamp.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(m.Timestamp) if err != nil { return nil, err } @@ -207,7 +214,7 @@ func (m *ServerFault) UnmarshalBinaryData(data []byte) (newData []byte, err erro if m.ServerID == nil { m.ServerID = primitives.NewZeroHash() } - newData, err = m.ServerID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(m.ServerID) if err != nil { return nil, err } @@ -215,21 +222,21 @@ func (m *ServerFault) UnmarshalBinaryData(data []byte) (newData []byte, err erro if m.AuditServerID == nil { m.AuditServerID = primitives.NewZeroHash() } - newData, err = m.AuditServerID.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(m.AuditServerID) if err != nil { return nil, err } - m.VMIndex, newData = newData[0], newData[1:] - m.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - m.Height, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + m.VMIndex, err = buf.PopByte() + m.DBHeight, err = buf.PopUInt32() + m.Height, err = buf.PopUInt32() - newData, err = m.SignatureList.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(&m.SignatureList) if err != nil { return nil, err } - return newData, nil + return buf.DeepCopyBytes(), nil } func (m *ServerFault) UnmarshalBinary(data []byte) error { diff --git a/common/adminBlock/adminBlock.go b/common/adminBlock/adminBlock.go index aeed0555fc..6241fd19a8 100644 --- a/common/adminBlock/adminBlock.go +++ b/common/adminBlock/adminBlock.go @@ -319,24 +319,21 @@ func (b *AdminBlock) MarshalBinary() ([]byte, error) { // Marshal all the entries into their own thing (need the size) var buf2 primitives.Buffer for _, v := range b.ABEntries { - data, err := v.MarshalBinary() + err := buf2.PushBinaryMarshallable(v) if err != nil { return nil, err } - buf2.Write(data) } b.GetHeader().SetMessageCount(uint32(len(b.ABEntries))) b.GetHeader().SetBodySize(uint32(buf2.Len())) - data, err := b.GetHeader().MarshalBinary() + var buf primitives.Buffer + err := buf.PushBinaryMarshallable(b.GetHeader()) if err != nil { return nil, err } - var buf primitives.Buffer - buf.Write(data) - // Write the Body out buf.Write(buf2.DeepCopyBytes()) @@ -353,23 +350,22 @@ func UnmarshalABlock(data []byte) (interfaces.IAdminBlock, error) { return block, nil } -func (b *AdminBlock) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Admin Block: %v", r) - } - }() - newData = data +func (b *AdminBlock) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) h := new(ABlockHeader) - newData, err = h.UnmarshalBinaryData(newData) + err := buf.PopBinaryMarshallable(h) if err != nil { - return + return nil, err } b.Header = h b.ABEntries = make([]interfaces.IABEntry, int(b.GetHeader().GetMessageCount())) for i := uint32(0); i < b.GetHeader().GetMessageCount(); i++ { - switch newData[0] { + t, err := buf.PeekByte() + if err != nil { + return nil, err + } + switch t { case constants.TYPE_MINUTE_NUM: b.ABEntries[i] = new(EndOfMinuteEntry) case constants.TYPE_DB_SIGNATURE: @@ -393,15 +389,16 @@ func (b *AdminBlock) UnmarshalBinaryData(data []byte) (newData []byte, err error case constants.TYPE_SERVER_FAULT: b.ABEntries[i] = new(ServerFault) default: - fmt.Printf("AB UNDEFINED ENTRY %x for block %v\n", newData[0], b.GetHeader().GetDBHeight()) + fmt.Printf("AB UNDEFINED ENTRY %x for block %v\n", t, b.GetHeader().GetDBHeight()) panic("Undefined Admin Block Entry Type") } - newData, err = b.ABEntries[i].UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(b.ABEntries[i]) if err != nil { - return + return nil, err } } - return + + return buf.DeepCopyBytes(), nil } // Read in the binary into the Admin block. diff --git a/common/adminBlock/adminBlockHeader.go b/common/adminBlock/adminBlockHeader.go index 95f2867dbb..83eeb40c02 100644 --- a/common/adminBlock/adminBlockHeader.go +++ b/common/adminBlock/adminBlockHeader.go @@ -5,11 +5,10 @@ package adminBlock import ( - "encoding/binary" + "bytes" "encoding/json" "fmt" - "bytes" "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" @@ -120,60 +119,87 @@ func (b *ABlockHeader) SetPrevBackRefHash(BackRefHash interfaces.IHash) { } // Write out the ABlockHeader to binary. -func (b *ABlockHeader) MarshalBinary() (data []byte, err error) { +func (b *ABlockHeader) MarshalBinary() ([]byte, error) { b.Init() var buf primitives.Buffer - data, err = b.GetAdminChainID().MarshalBinary() + err := buf.PushBinaryMarshallable(b.GetAdminChainID()) if err != nil { return nil, err } - buf.Write(data) - - data, err = b.PrevBackRefHash.MarshalBinary() + err = buf.PushBinaryMarshallable(b.PrevBackRefHash) + if err != nil { + return nil, err + } + err = buf.PushUInt32(b.DBHeight) if err != nil { return nil, err } - buf.Write(data) - - binary.Write(&buf, binary.BigEndian, b.DBHeight) - primitives.EncodeVarInt(&buf, b.HeaderExpansionSize) - buf.Write(b.HeaderExpansionArea) + err = buf.PushVarInt(b.HeaderExpansionSize) + if err != nil { + return nil, err + } + err = buf.Push(b.HeaderExpansionArea) + if err != nil { + return nil, err + } - binary.Write(&buf, binary.BigEndian, b.MessageCount) - binary.Write(&buf, binary.BigEndian, b.BodySize) + err = buf.PushUInt32(b.MessageCount) + if err != nil { + return nil, err + } + err = buf.PushUInt32(b.BodySize) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), err } -func (b *ABlockHeader) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Admin Block Header: %v", r) - } - }() - newData = data - newData, err = b.GetAdminChainID().UnmarshalBinaryData(newData) +func (b *ABlockHeader) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + h := primitives.NewZeroHash() + err := buf.PopBinaryMarshallable(h) if err != nil { - return + return nil, err + } + if h.String() != "000000000000000000000000000000000000000000000000000000000000000a" { + return nil, fmt.Errorf("Block does not begin with the ABlock ChainID") } b.PrevBackRefHash = new(primitives.Hash) - newData, err = b.PrevBackRefHash.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(b.PrevBackRefHash) if err != nil { - return + return nil, err } - b.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + b.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err + } - b.HeaderExpansionSize, newData = primitives.DecodeVarInt(newData) - b.HeaderExpansionArea, newData = newData[:b.HeaderExpansionSize], newData[b.HeaderExpansionSize:] + b.HeaderExpansionSize, err = buf.PopVarInt() + if err != nil { + return nil, err + } + if b.HeaderExpansionSize > 0 { + b.HeaderExpansionArea, err = buf.PopLen(int(b.HeaderExpansionSize)) + if err != nil { + return nil, err + } + } - b.MessageCount, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - b.BodySize, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + b.MessageCount, err = buf.PopUInt32() + if err != nil { + return nil, err + } + b.BodySize, err = buf.PopUInt32() + if err != nil { + return nil, err + } - return + return buf.DeepCopyBytes(), nil } // Read in the binary into the ABlockHeader. diff --git a/common/adminBlock/adminBlock_test.go b/common/adminBlock/adminBlock_test.go index 3ff13a500e..1af98d08b5 100644 --- a/common/adminBlock/adminBlock_test.go +++ b/common/adminBlock/adminBlock_test.go @@ -500,29 +500,57 @@ func TestABlockHeaderMarshalUnmarshal(t *testing.T) { } +type TestABlock struct { + Raw string + KeyMR string + Hash string +} + func TestUnmarshalABlock(t *testing.T) { - raw := "000000000000000000000000000000000000000000000000000000000000000a0a9aa1efbe7d0e8d9c1d460d1c78e3e7b50f984e65a3f3ee7b73100a94189dbf000000010000000002000000830100000000000000000000000000000000000000000000000000000000000000000426a802617848d4d16d87830fc521f4d136bb2d0c352850919c2679f189613a83efbcbed19b5842e5aa06e66c41d8b61826d95d50c1cbc8bd5373f986c370547133462a9ffa0dcff025a6ad26747c95f1bdd88e2596fc8c6eaa8a2993c72c050002" - b, err := hex.DecodeString(raw) - if err != nil { - t.Error(err) - } - a, err := UnmarshalABlock(b) - if err != nil { - t.Error(err) - } - h, err := a.LookupHash() - if err != nil { - t.Error(err) - } - if h.String() != "b30ab81a8afdbe0be1627ef151bf7e263ce3d39d60b61464d81daa8320c28a4f" { - t.Error("Invalid Hash") - } - h, err = a.BackReferenceHash() - if err != nil { - t.Error(err) - } - if h.String() != "b2405450392038716e9b24804345f9ac0736792dba436c024268ed8100683894" { - t.Error("Invalid KeyMR") + ts := []TestABlock{} + + t1 := TestABlock{} + t1.Raw = "000000000000000000000000000000000000000000000000000000000000000a0a9aa1efbe7d0e8d9c1d460d1c78e3e7b50f984e65a3f3ee7b73100a94189dbf000000010000000002000000830100000000000000000000000000000000000000000000000000000000000000000426a802617848d4d16d87830fc521f4d136bb2d0c352850919c2679f189613a83efbcbed19b5842e5aa06e66c41d8b61826d95d50c1cbc8bd5373f986c370547133462a9ffa0dcff025a6ad26747c95f1bdd88e2596fc8c6eaa8a2993c72c050002" + t1.KeyMR = "b30ab81a8afdbe0be1627ef151bf7e263ce3d39d60b61464d81daa8320c28a4f" + t1.Hash = "b2405450392038716e9b24804345f9ac0736792dba436c024268ed8100683894" + ts = append(ts, t1) + + t2 := TestABlock{} + t2.Raw = "000000000000000000000000000000000000000000000000000000000000000a4a994fbc66747f91e28576e715de510bb12937ee68885ffc0a70ad3b03b375600001131100000000490000107706888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f40001131208888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f400f8139f98fadc948b254d0dea29c55fab7fa14f1fd97ef78ef7bb99d2d82bd6f10001131203888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f486e2f9073dfafb461888955166c12c6b1d9aa98504af1cccb08f0ad53fbbb66609888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f400005bf09c36ebb93643acf41e716261357583ee728106888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac10001131208888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac10007f339e556ee999cc7e33500753ea0933381b09f5c2bca26e224d716e61a88620001131203888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac17c45e29fd0c7e09428e7ea60ed5042e8a0d6a091cc576e255eb10b7e899d3c0309888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac100006788c85b7963c8527900a2a2ad2c24d15f347d89068888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f900011312088888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f9002001c69d076a5bf43335d41f49ad7626f1d79d8e1dfe9d9f9c8cc9a0d99efd5b00011312038888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f9914ab0fd1905f3ef19e54f94dd3caee1055793eb8cd5ce7f982cd15ea393bcd7098888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f90000c568a1206e29c7c8fed15aee12515833434b4eb406888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb7341560001131208888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb73415600646f6bf2eaa80a803f1ffd3286945c4d6ddfdf5974177a52141c6906153f52370001131203888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb734156002762ccf5948b8e1c29a9c3df4748cf3efe6567eb3046e6361f353079e5534409888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb73415600007c6b5121835d148932c75ce773208ffc17a4144f068888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a8264537600011312088888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a82645376000d6a22b9bf17851c830189fb324ba7d1ea8d6a15eea3adf671109825a133214700011312038888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a82645376fe21f1320ff7eaaab9ceb9551833078ab79b5b0dfe86097a88ca26d74e48b354098888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a826453760000b4db03e03da3555f630aef3900897e67247c847706888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda70001131208888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda700e3b88b704533612f69b5d6390737481694d7d8acb71e532cac3e8dd2d11ca6910001131203888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda71cbf54effa547cf89751e3a02d8980ea7e9325e591ff8f1d360bbe323da8fa5a09888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda70000dcb4dcd7e5a518854eadd0ec48955101d9fbac350688888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc34000113120888888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3400667a53519cab0365d1a1ac625b6cd64d86695e8ae38d280ea6d3dbe8191acf34000113120388888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3452103541ebcd32f5a55dc3c5037fd6396bbe3d65d22f8c06026a9ad97440d8cd0988888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3400005ba2689c372fdf712e477a83059a5da313e07bf0068888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d737300011312088888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d737300296d08be4a741d6c328ab47d80a55590dceef6550066a0a76e4816a3f51eefee00011312038888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d7373a5f91355b6c8a1a9b38d378434886caea05cc73e544416ec4c9b7f219f23c497098888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d73730000850fd39e1841b29c12f4ace379380a467489dba806888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb350001131208888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb3500c2bbab9d274415765eae5c3ee3b94ff3c38dd5c9b02c8f842e2770a6de0b50680001131203888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb3586400145400bf22a717d1bd4fc7f15e5de2872d21e815bc0a4916c15de2e6eb709888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb350000e0e135c1ee0c2131b2dac5fcb353863ac21fff62068888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b471400011312088888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b47140093f6aca96b011fc31fd655fee9556b459509308eaaa63c02e9ebff8f384c72e000011312038888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b471435b100ead1d81fe3a3e6b1a656c127b14a2ef9d520adec6ea0d7b9d1d5488268098888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b4714000058e737d93cb52102d78ee7b918bd33a4412f901e06888888f05308313f6e8f5619cacfb32e0dcba25b4741de9c0fc3b127e8ba2a6b0001131208888888f05308313f6e8f5619cacfb32e0dcba25b4741de9c0fc3b127e8ba2a6b006ceeb261cc19b14f6c89bb0bd937f195ffc9e6adaa5618e432752b01a00792c70001131203888888f05308313f6e8f5619cacfb32e0dcba25b4741de9c0fc3b127e8ba2a6b8fcab189bbb2f97249d05b0b31adeaef23b7aaca326673e16fc901022f8285c809888888f05308313f6e8f5619cacfb32e0dcba25b4741de9c0fc3b127e8ba2a6b000057b3621913fd321c4c4f07cef3468bf04b0baf59068888881541fc5bc1bcc0597e71eed5df7de8d47c8eb97f867d16ebb20781f38b00011312088888881541fc5bc1bcc0597e71eed5df7de8d47c8eb97f867d16ebb20781f38b0034ffc2a7f6e35e503fd2d4259113d4d9b131e8e56d63a1c277ab5064d58d982600011312038888881541fc5bc1bcc0597e71eed5df7de8d47c8eb97f867d16ebb20781f38b1ce468172d6408643a8931838a935733f6fa97d02a8b44a741a1376da8829152098888881541fc5bc1bcc0597e71eed5df7de8d47c8eb97f867d16ebb20781f38b0000010c53bd5e4a863cf8e7df48f567e3f2e492aba906888888c1fd1cf7ca3e0c4e2e9a6462aa8ac4e537563ee54ff41eb6b617a1ec370001131208888888c1fd1cf7ca3e0c4e2e9a6462aa8ac4e537563ee54ff41eb6b617a1ec3700b9a4837383cf11d818f1c1931f5586f840967fe0931d9b733394f75bf39fcd170001131203888888c1fd1cf7ca3e0c4e2e9a6462aa8ac4e537563ee54ff41eb6b617a1ec3796fa0827f28ced76f18e42b8ef836d96c5c5adde4b8c98a406ad00610998562809888888c1fd1cf7ca3e0c4e2e9a6462aa8ac4e537563ee54ff41eb6b617a1ec3700001f605e0d687dbb731e6961cdf8c30e24195889d006888888b2ddad8c24fdf3033bdf6bd9c393ffe074b6f5d5741c84afea27c1656e0001131208888888b2ddad8c24fdf3033bdf6bd9c393ffe074b6f5d5741c84afea27c1656e00b11d2c22e96af34946810c816ada60a7027ed3d7c98aac72283ed348fc58cf730001131203888888b2ddad8c24fdf3033bdf6bd9c393ffe074b6f5d5741c84afea27c1656e74055ead8eb83d34515c66bb7824dfda3659e1193dd31f6f38eed6e2cdc4e59209888888b2ddad8c24fdf3033bdf6bd9c393ffe074b6f5d5741c84afea27c1656e000072f4aa05adc0b5284602bd744858106c618b932e06888888a8da713519881065d90f73f498b36d956e3390c5a6c06747922395075f0001131208888888a8da713519881065d90f73f498b36d956e3390c5a6c06747922395075f00ffb9efd4d490535e3b5041622354f5c440524b0d1976582e0c9ba6cb1649279b0001131203888888a8da713519881065d90f73f498b36d956e3390c5a6c06747922395075f36108e2fd7ba67a25886c14408db1bc2a1d0098a23f2b64e4734ff80b772def009888888a8da713519881065d90f73f498b36d956e3390c5a6c06747922395075f00003d5ffebea388ce494cd7d24ff03165117561ef9006888888a5ce32a3a257c1ff29033b6c01dd20239e7c68ebaf06d690e4ae2b7e830001131208888888a5ce32a3a257c1ff29033b6c01dd20239e7c68ebaf06d690e4ae2b7e830013d42208f7a7699c7976dc19424872268e503779850fb72aecae4b5341dd40c70001131203888888a5ce32a3a257c1ff29033b6c01dd20239e7c68ebaf06d690e4ae2b7e83611fb3b711629ee6964f6e6d7a7a389ab275b4b14c8eafaaa72930f2b9c1230309888888a5ce32a3a257c1ff29033b6c01dd20239e7c68ebaf06d690e4ae2b7e830000412945af7b4ec2ff17285b22631be19f3201d572068888886043746fe47dcf55952b20d8aee6ae842024010fd3f42bc0076a502f4200011312088888886043746fe47dcf55952b20d8aee6ae842024010fd3f42bc0076a502f4200847ef7a9d15df05940a97030a7b783fad54622bdb81f5698f948b94e127eb6e500011312038888886043746fe47dcf55952b20d8aee6ae842024010fd3f42bc0076a502f42b566f30f2013dc3cf7960268da70efb76534ce710f270c1b3ae08781f9faae1b098888886043746fe47dcf55952b20d8aee6ae842024010fd3f42bc0076a502f420000e2a977f66a529d3746727f390c429298f6daef680688888870cf06fb3ed94af3ba917dbe9fab73391915c57812bd6606e6ced76d53000113120888888870cf06fb3ed94af3ba917dbe9fab73391915c57812bd6606e6ced76d53005413e626ce80d90276b5b2388d13f4a4dce2faffce6bb76b9290fcd11dd700dc000113120388888870cf06fb3ed94af3ba917dbe9fab73391915c57812bd6606e6ced76d53151253cf6f9ad8db3f1bd7116a6ec894851fff4268ad1c14fe3ce8f3933a9b080988888870cf06fb3ed94af3ba917dbe9fab73391915c57812bd6606e6ced76d53000080b560002d85154fa1c255531c232f84b4293c860100000000000000000000000000000000000000000000000000000000000000000426a802617848d4d16d87830fc521f4d136bb2d0c352850919c2679f189613a9c798bf48022895c046a4e780fc43a4f112d90d79889a335ea88336e1cb01e47862bb4504a70cdd3055c9718b2b68166142229f66d772cccb2cac5e5b8dd6c09" + t2.KeyMR = "748a13e79aa35130ea193141ee7849b5cc7ffcceb1aa77d58cb62c129170ca79" + t2.Hash = "4f4ba20e4d8e62dd10827b20523f084ed3d5a90164bd06b95557109820ae0416" + ts = append(ts, t2) + + t3 := TestABlock{} + t3.Raw = "000000000000000000000000000000000000000000000000000000000000000a9ac526b050fbebf858a8e755e23655d3ccb53c256dac855304c74989de15d1810001131300000000290000095f05888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f40001131408888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f400f8139f98fadc948b254d0dea29c55fab7fa14f1fd97ef78ef7bb99d2d82bd6f10001131403888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f486e2f9073dfafb461888955166c12c6b1d9aa98504af1cccb08f0ad53fbbb66609888888655866a003faabd999c7b0a7c908af17d63fd2ac2951dc99e1ad2a14f400005bf09c36ebb93643acf41e716261357583ee728105888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac10001131408888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac10007f339e556ee999cc7e33500753ea0933381b09f5c2bca26e224d716e61a88620001131403888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac17c45e29fd0c7e09428e7ea60ed5042e8a0d6a091cc576e255eb10b7e899d3c0309888888dda15d7ad44c3286d66cc4f82e6fc07ed88de4d13ac9a182199593cac100006788c85b7963c8527900a2a2ad2c24d15f347d89058888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f900011314088888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f9002001c69d076a5bf43335d41f49ad7626f1d79d8e1dfe9d9f9c8cc9a0d99efd5b00011314038888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f9914ab0fd1905f3ef19e54f94dd3caee1055793eb8cd5ce7f982cd15ea393bcd7098888889585051d7117d217a55a366d56826eda35c951f02428b976524dbfc7f90000c568a1206e29c7c8fed15aee12515833434b4eb405888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb7341560001131408888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb73415600646f6bf2eaa80a803f1ffd3286945c4d6ddfdf5974177a52141c6906153f52370001131403888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb734156002762ccf5948b8e1c29a9c3df4748cf3efe6567eb3046e6361f353079e5534409888888bf5e39211db27b2d2b1b57606b4d68cf57e908971949a233d8eb73415600007c6b5121835d148932c75ce773208ffc17a4144f058888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a8264537600011314088888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a82645376000d6a22b9bf17851c830189fb324ba7d1ea8d6a15eea3adf671109825a133214700011314038888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a82645376fe21f1320ff7eaaab9ceb9551833078ab79b5b0dfe86097a88ca26d74e48b354098888886ff14cef50365b785eb3cefab5bc30175d022be06ed412391a826453760000b4db03e03da3555f630aef3900897e67247c847705888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda70001131408888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda700e3b88b704533612f69b5d6390737481694d7d8acb71e532cac3e8dd2d11ca6910001131403888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda71cbf54effa547cf89751e3a02d8980ea7e9325e591ff8f1d360bbe323da8fa5a09888888b1255ea1cc0b3ab3b9425d3239643ae1f3c00ec634690eda784f05bda70000dcb4dcd7e5a518854eadd0ec48955101d9fbac350588888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc34000113140888888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3400667a53519cab0365d1a1ac625b6cd64d86695e8ae38d280ea6d3dbe8191acf34000113140388888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3452103541ebcd32f5a55dc3c5037fd6396bbe3d65d22f8c06026a9ad97440d8cd0988888841ac82c501a300def3e95d724b4b5e31f729f3b6d9d9736dca0f0edc3400005ba2689c372fdf712e477a83059a5da313e07bf0058888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d737300011314088888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d737300296d08be4a741d6c328ab47d80a55590dceef6550066a0a76e4816a3f51eefee00011314038888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d7373a5f91355b6c8a1a9b38d378434886caea05cc73e544416ec4c9b7f219f23c497098888882fa588e8ad6e73555a9b9ff3d84b468601b81328ec09d91051369d73730000850fd39e1841b29c12f4ace379380a467489dba805888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb350001131408888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb3500c2bbab9d274415765eae5c3ee3b94ff3c38dd5c9b02c8f842e2770a6de0b50680001131403888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb3586400145400bf22a717d1bd4fc7f15e5de2872d21e815bc0a4916c15de2e6eb709888888b4eecb6868615e1875120e855529b4e372e2887cdec7185b46abfcfb350000e0e135c1ee0c2131b2dac5fcb353863ac21fff62058888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b471400011314088888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b47140093f6aca96b011fc31fd655fee9556b459509308eaaa63c02e9ebff8f384c72e000011314038888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b471435b100ead1d81fe3a3e6b1a656c127b14a2ef9d520adec6ea0d7b9d1d5488268098888884a0acbf1a23e3291b99681b80a91ca51914d64e39de65645868e0b4714000058e737d93cb52102d78ee7b918bd33a4412f901e0100000000000000000000000000000000000000000000000000000000000000000426a802617848d4d16d87830fc521f4d136bb2d0c352850919c2679f189613ab50980c62e82c822a5801de348ebbc8072510654737f45420adf19a273cbc50657fe854181e0597a71ecce36ae3763c7f0bcfea270d441212a15ebf6f313e707" + t3.KeyMR = "c4994ca612791460f4687d68cc351bdb183636d2f5300dbf3b8e58811171b39c" + t3.Hash = "336c9f4c143be396afb2fb112e18777da000883e576d2c9801c51a0f1d7cb7bf" + ts = append(ts, t3) + + for _, tBlock := range ts { + raw := tBlock.Raw + b, err := hex.DecodeString(raw) + if err != nil { + t.Error(err) + } + a, err := UnmarshalABlock(b) + if err != nil { + t.Error(err) + } + h, err := a.LookupHash() + if err != nil { + t.Error(err) + } + if h.String() != tBlock.KeyMR { + t.Error("Invalid Hash") + } + h, err = a.BackReferenceHash() + if err != nil { + t.Error(err) + } + if h.String() != tBlock.Hash { + t.Error("Invalid KeyMR") + } } } diff --git a/common/directoryBlock/directoryBlock.go b/common/directoryBlock/directoryBlock.go index 6886412c00..1017db91ac 100644 --- a/common/directoryBlock/directoryBlock.go +++ b/common/directoryBlock/directoryBlock.go @@ -108,6 +108,7 @@ func (c *DirectoryBlock) GetEntrySigHashes() []interfaces.IHash { return nil } +//bubble sort func (c *DirectoryBlock) Sort() { done := false for i := 3; !done && i < len(c.DBEntries)-1; i++ { @@ -156,8 +157,23 @@ func (c *DirectoryBlock) GetEBlockDBEntries() []interfaces.IDBEntry { return answer } -func (c *DirectoryBlock) GetKeyMR() interfaces.IHash { +func (c *DirectoryBlock) CheckDBEntries() error { + if len(c.DBEntries) < 3 { + return fmt.Errorf("Not enough entries - %v", len(c.DBEntries)) + } + if c.DBEntries[0].GetChainID().String() != "000000000000000000000000000000000000000000000000000000000000000a" { + return fmt.Errorf("Invalid ChainID at position 0 - %v", c.DBEntries[0].GetChainID().String()) + } + if c.DBEntries[1].GetChainID().String() != "000000000000000000000000000000000000000000000000000000000000000c" { + return fmt.Errorf("Invalid ChainID at position 1 - %v", c.DBEntries[1].GetChainID().String()) + } + if c.DBEntries[2].GetChainID().String() != "000000000000000000000000000000000000000000000000000000000000000f" { + return fmt.Errorf("Invalid ChainID at position 2 - %v", c.DBEntries[2].GetChainID().String()) + } + return nil +} +func (c *DirectoryBlock) GetKeyMR() interfaces.IHash { keyMR, err := c.BuildKeyMerkleRoot() if err != nil { panic("Failed to build the key MR") @@ -247,33 +263,32 @@ func (e *DirectoryBlock) String() string { } -func (b *DirectoryBlock) MarshalBinary() (data []byte, err error) { +func (b *DirectoryBlock) MarshalBinary() ([]byte, error) { b.Init() - var buf primitives.Buffer - b.Sort() + _, err := b.BuildBodyMR() + if err != nil { + return nil, err + } - b.BuildBodyMR() + buf := primitives.NewBuffer(nil) - data, err = b.GetHeader().MarshalBinary() + err = buf.PushBinaryMarshallable(b.GetHeader()) if err != nil { - return + return nil, err } - buf.Write(data) for i := uint32(0); i < b.Header.GetBlockCount(); i++ { - data, err = b.GetDBEntries()[i].MarshalBinary() + err = buf.PushBinaryMarshallable(b.GetDBEntries()[i]) if err != nil { - return + return nil, err } - buf.Write(data) } return buf.DeepCopyBytes(), err } func (b *DirectoryBlock) BuildBodyMR() (interfaces.IHash, error) { - count := uint32(len(b.GetDBEntries())) b.GetHeader().SetBlockCount(count) if count == 0 { @@ -348,20 +363,13 @@ func UnmarshalDBlock(data []byte) (interfaces.IDirectoryBlock, error) { return dBlock, nil } -func (b *DirectoryBlock) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Directory Block: %v", r) - } - }() - - newData = data - +func (b *DirectoryBlock) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) var fbh interfaces.IDirectoryBlockHeader = new(DBlockHeader) - newData, err = fbh.UnmarshalBinaryData(newData) + err := buf.PopBinaryMarshallable(fbh) if err != nil { - return + return nil, err } b.SetHeader(fbh) @@ -369,18 +377,23 @@ func (b *DirectoryBlock) UnmarshalBinaryData(data []byte) (newData []byte, err e entries := make([]interfaces.IDBEntry, count) for i := uint32(0); i < count; i++ { entries[i] = new(DBEntry) - newData, err = entries[i].UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(entries[i]) if err != nil { - return + return nil, err } } err = b.SetDBEntries(entries) if err != nil { - return + return nil, err } - return + err = b.CheckDBEntries() + if err != nil { + return nil, err + } + + return buf.DeepCopyBytes(), nil } func (h *DirectoryBlock) GetTimestamp() interfaces.Timestamp { diff --git a/common/directoryBlock/directoryBlockEntry.go b/common/directoryBlock/directoryBlockEntry.go index 0b05531097..3065500aac 100644 --- a/common/directoryBlock/directoryBlockEntry.go +++ b/common/directoryBlock/directoryBlockEntry.go @@ -7,7 +7,6 @@ package directoryBlock import ( "fmt" - "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" ) @@ -23,6 +22,15 @@ var _ interfaces.Printable = (*DBEntry)(nil) var _ interfaces.BinaryMarshallable = (*DBEntry)(nil) var _ interfaces.IDBEntry = (*DBEntry)(nil) +func (c *DBEntry) Init() { + if c.ChainID == nil { + c.ChainID = primitives.NewZeroHash() + } + if c.KeyMR == nil { + c.KeyMR = primitives.NewZeroHash() + } +} + func (a *DBEntry) IsSameAs(b interfaces.IDBEntry) bool { if a == nil || b == nil { if a == nil && b == nil { @@ -56,48 +64,37 @@ func (c *DBEntry) SetKeyMR(keyMR interfaces.IHash) { c.KeyMR = keyMR } -func (e *DBEntry) MarshalBinary() (data []byte, err error) { - var buf primitives.Buffer +func (e *DBEntry) MarshalBinary() ([]byte, error) { + e.Init() + buf := primitives.NewBuffer(nil) - data, err = e.ChainID.MarshalBinary() + err := buf.PushBinaryMarshallable(e.ChainID) if err != nil { - return + return nil, err } - buf.Write(data) - if e.KeyMR == nil { - data, err = primitives.NewHash(constants.ZERO_HASH).MarshalBinary() - } else { - data, err = e.KeyMR.MarshalBinary() - } + err = buf.PushBinaryMarshallable(e.KeyMR) if err != nil { - return + return nil, err } - buf.Write(data) return buf.DeepCopyBytes(), nil } -func (e *DBEntry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Directory Block Entry: %v", r) - } - }() - newData = data - e.ChainID = new(primitives.Hash) - newData, err = e.ChainID.UnmarshalBinaryData(newData) +func (e *DBEntry) UnmarshalBinaryData(data []byte) ([]byte, error) { + e.Init() + buf := primitives.NewBuffer(data) + + err := buf.PopBinaryMarshallable(e.ChainID) if err != nil { - return + return nil, err } - - e.KeyMR = new(primitives.Hash) - newData, err = e.KeyMR.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(e.KeyMR) if err != nil { - return + return nil, err } - return + return buf.DeepCopyBytes(), nil } func (e *DBEntry) UnmarshalBinary(data []byte) (err error) { diff --git a/common/directoryBlock/directoryBlockHeader.go b/common/directoryBlock/directoryBlockHeader.go index 63cd013076..16528d5912 100644 --- a/common/directoryBlock/directoryBlockHeader.go +++ b/common/directoryBlock/directoryBlockHeader.go @@ -5,7 +5,6 @@ package directoryBlock import ( - "encoding/binary" "encoding/json" "fmt" @@ -177,34 +176,40 @@ func (e *DBlockHeader) String() string { func (b *DBlockHeader) MarshalBinary() ([]byte, error) { b.Init() - var buf primitives.Buffer + buf := primitives.NewBuffer(nil) - buf.WriteByte(b.Version) - binary.Write(&buf, binary.BigEndian, b.NetworkID) - - data, err := b.BodyMR.MarshalBinary() + err := buf.PushByte(b.Version) if err != nil { return nil, err } - buf.Write(data) - - data, err = b.PrevKeyMR.MarshalBinary() + err = buf.PushUInt32(b.NetworkID) if err != nil { return nil, err } - buf.Write(data) - - data, err = b.PrevFullHash.MarshalBinary() + err = buf.PushBinaryMarshallable(b.BodyMR) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(b.PrevKeyMR) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(b.PrevFullHash) + if err != nil { + return nil, err + } + err = buf.PushUInt32(b.Timestamp) + if err != nil { + return nil, err + } + err = buf.PushUInt32(b.DBHeight) + if err != nil { + return nil, err + } + err = buf.PushUInt32(b.BlockCount) if err != nil { return nil, err } - buf.Write(data) - - binary.Write(&buf, binary.BigEndian, b.Timestamp) - - binary.Write(&buf, binary.BigEndian, b.DBHeight) - - binary.Write(&buf, binary.BigEndian, b.BlockCount) if b.BlockCount > 100000 { panic("Send: Blockcount too great in directory block") @@ -213,46 +218,53 @@ func (b *DBlockHeader) MarshalBinary() ([]byte, error) { return buf.DeepCopyBytes(), err } -func (b *DBlockHeader) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling Directory Block Header: %v", r) - } - }() - - // fmt.Printf("Unmarshal %x\n",data[:113]) +func (b *DBlockHeader) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) + var err error - newData = data - b.Version, newData = newData[0], newData[1:] - - b.NetworkID, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + b.Version, err = buf.PopByte() + if err != nil { + return nil, err + } + b.NetworkID, err = buf.PopUInt32() + if err != nil { + return nil, err + } b.BodyMR = new(primitives.Hash) - newData, err = b.BodyMR.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(b.BodyMR) if err != nil { - return + return nil, err } - b.PrevKeyMR = new(primitives.Hash) - newData, err = b.PrevKeyMR.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(b.PrevKeyMR) if err != nil { - return + return nil, err } - b.PrevFullHash = new(primitives.Hash) - newData, err = b.PrevFullHash.UnmarshalBinaryData(newData) + err = buf.PopBinaryMarshallable(b.PrevFullHash) if err != nil { - return + return nil, err } - b.Timestamp, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - b.DBHeight, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] - b.BlockCount, newData = binary.BigEndian.Uint32(newData[0:4]), newData[4:] + b.Timestamp, err = buf.PopUInt32() + if err != nil { + return nil, err + } + b.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err + } + b.BlockCount, err = buf.PopUInt32() + if err != nil { + return nil, err + } if b.BlockCount > 100000 { panic("Receive: Blockcount too great in directory block" + fmt.Sprintf(":::: %d", b.BlockCount)) } - return + + return buf.DeepCopyBytes(), nil } func (b *DBlockHeader) UnmarshalBinary(data []byte) (err error) { diff --git a/common/directoryBlock/directoryBlock_test.go b/common/directoryBlock/directoryBlock_test.go index a657a64d23..0833211e70 100644 --- a/common/directoryBlock/directoryBlock_test.go +++ b/common/directoryBlock/directoryBlock_test.go @@ -521,3 +521,152 @@ Entries: t.Errorf("CheckBlockPairIntegrity(db2, db1) failed") } } + +func TestSortFunc(t *testing.T) { + db1 := NewDirectoryBlock(nil) + db1.(*DirectoryBlock).Init() + + ecb_kmr, _ := primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000050") + cid, _ := primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000150") + db1.SetEntryHash(ecb_kmr, cid, 3) + + ecb_kmr, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000040") + cid, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000140") + db1.SetEntryHash(ecb_kmr, cid, 4) + + ecb_kmr, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000030") + cid, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000130") + db1.SetEntryHash(ecb_kmr, cid, 5) + + //should be out of order at this point + //fmt.Println(db1.GetEntryHashes()) + + //marshal will call the sort function, which does not have an interface + db1.MarshalBinary() + //fmt.Println(db1.GetEntryHashes()) + expected := "[0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000030 0000000000000000000000000000000000000000000000000000000000000040 0000000000000000000000000000000000000000000000000000000000000050]" + + sorted := fmt.Sprint(db1.GetEntryHashes()) + if sorted != expected { + t.Errorf("Sort function failed") + } +} + +func TestMerkleTree(t *testing.T) { + db1 := NewDirectoryBlock(nil) + db1.(*DirectoryBlock).Init() + + ecb_kmr, _ := primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000010") + cid, _ := primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000100") + db1.SetEntryHash(ecb_kmr, cid, 3) + + ecb_kmr, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000020") + cid, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000200") + db1.SetEntryHash(ecb_kmr, cid, 4) + + ecb_kmr, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000030") + cid, _ = primitives.HexToHash("0000000000000000000000000000000000000000000000000000000000000300") + db1.SetEntryHash(ecb_kmr, cid, 5) + + //should be out of order at this point + //fmt.Println(db1.GetEntryHashes()) + mr, err := db1.BuildBodyMR() + if err != nil { + t.Errorf("tree building function failed", err) + } + + //fmt.Println(mr) + + expected := "a8caad450c7f721526087bf33d251d5fd537e51885f718e89dd9780cfeee3e66" + + value := fmt.Sprint(mr) + if value != expected { + t.Errorf("Sort function failed") + } +} + +func TestSameAs(t *testing.T) { + //block 1000 + db1kbytes, _ := hex.DecodeString("00fa92e5a2f2eaf170a2da9e4956a40231ed7255c6c6e5ada1ed746fc5ab3a0b79b8c700367a49467be900ba00daedd7d9cf2b1a07f839360e859e1f3d78c46701d3ad1507974595bf9b73dbec9ff5d5744cbf6410d66b837924208a0b8b84e54fc4aad660016ea716000003e800000004000000000000000000000000000000000000000000000000000000000000000a3d92dc70f4cfd4fe464e18962057d71924679cc866fe37f4b8023d292d9f34ce000000000000000000000000000000000000000000000000000000000000000c0526c1fdb9e0813e297a331891815ed893cb5a9cff15529197f13932ed9f9547000000000000000000000000000000000000000000000000000000000000000f526aca5f63bfb59188bae1fc367411a123bcc1d5a3c23c710b66b46703542855df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e604f08c42bc44c09ac26c349bef8ee80d2ffb018cfa3e769107b2413792fa9bd642") + db1k, err := UnmarshalDBlock(db1kbytes) + if err != nil { + t.Errorf("db unmarshall failed") + } + + db1k1bytes, _ := hex.DecodeString("00fa92e5a21b06a03dcc94ab7d6d991bfc9937cf72c744095f46316ef2281f9f20d3738c03cd45e38f53c090a03513f0c67afb93c774a064a5614a772cd079f31b3db4d01106e8d2d429fe728c4a90a3b6fbd910eb97e543c460c762a72d1563302bb401b1016ea720000003e900000004000000000000000000000000000000000000000000000000000000000000000abfd0f8ab0ea25a16d68c0533401eaea53cb84b9b597f2ad85006120836ac1788000000000000000000000000000000000000000000000000000000000000000c9495cc2988fb7d598cec0c91ea00447e9f2e6b0f5cd3b808e50eb7010cae4202000000000000000000000000000000000000000000000000000000000000000f1f67dc478fc7a5f8d9d612e362040a114dd437228c64b11a10df3a3e31d01c45df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e604cbf7179a054e6a40dbbebdb4ac29e5185052889907c8607f35a3aca84eeb72f6") + db1k1, err := UnmarshalDBlock(db1k1bytes) + if err != nil { + t.Errorf("db unmarshall failed") + } + + if db1k.IsSameAs(db1k1) { + t.Errorf("directory block same as comparison failed") + } + + db1kclone, err := UnmarshalDBlock(db1kbytes) + if err != nil { + t.Errorf("db unmarshall failed") + } + + if !db1k.IsSameAs(db1kclone) { + t.Errorf("directory block same as comparison failed") + } + //fmt.Println(db1k1) +} + +type TestDBlock struct { + Raw string + KeyMR string + Hash string +} + +func TestMarshalUnmarshalFixedDirectoryBlocks(t *testing.T) { + ts := []TestDBlock{} + + t1 := TestDBlock{} + t1.Raw = "00fa92e5a24d0789d16890ec1f96f617d8c802a40ee876d761b076da330d784356ac80f9ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016e80100000000000000003000000000000000000000000000000000000000000000000000000000000000a4fb409d5369fad6aa7768dc620f11cd219f9b885956b631ad050962ca934052e000000000000000000000000000000000000000000000000000000000000000cf87cfc073df0e82cdc2ed0bb992d7ea956fd32b435b099fc35f4b0696948507a000000000000000000000000000000000000000000000000000000000000000fa164ccbb77a21904edc4f2bb753aa60635fb2b60279c06ae01aa211f37541736" + t1.KeyMR = "64d4352b134280305599363ea388c2a9c3c64dc3ee6e0100893262e372bf064b" + t1.Hash = "cbd3d09db6defdc25dfc7d57f3479b339a077183cd67022e6d1ef6c041522b40" + ts = append(ts, t1) + + t2 := TestDBlock{} + t2.Raw = "00fa92e5a2c533096d61aad4d07e85546c813a14ff6aeccf09af5af888e22c66a4bd8d8e682079bf4c6ae92c398eb16cf3b10ba05d0ac0691c07e5e023d527dc09c562b9c48fb437fae06ef1b38d971eb0edf53e7b9e4d5737aef4aafa6d6e18b33723cd9d017c4ec300015e5e0000003d000000000000000000000000000000000000000000000000000000000000000ad93677fa2660ee262b0ac4f941e71c2925c3193de5bb6ef5480c790c3cc3f1fc000000000000000000000000000000000000000000000000000000000000000c76bd8907808e6d94e1c37c2338a02bcbc21dd87dac1c1d75bdbc524663e082eb000000000000000000000000000000000000000000000000000000000000000f3689c1bdc32b75c43091944d789149bcbf8fe75eff741a8abb5827e269a1d09f0464bf13a66ed62d8196c51292caedaecbd8dfe245acdbd1aafdac9ed9d77b1bad5aa08b6c601a2f116374818fb99ee82edd0b9caecb443cb97bebc6c970e07204e57c0c2fee5834c885c16b88301ffaa4596b40fca01e91a1f28e4492ce09e8148a8e45ad34a64225c4da7d7b5bf1b126cfc6a9c8f0c1114265c987e3ea4d0106a40590f536293bdecc3d7e69a5c21785c6ed454a59caf7b2e083a1a88ac85b497ac429de804506d7828d173898ed061c2d6153a0eb001cd226756abc3563310caff62ea5b5aa015c706add7b2463a5be07e1f0537617f553558090f23c7f567ef00d23f177b34bd04dd80fb8226581bb24d2e8d1ea938d320c89a1ac8dd0c90dc47da47563719e91de700d54f7f0423dd142d2f339a88c238509b30ab2d353be86b8fd7f451ca0aa6394151373624d4d3050e4f3ad1f441e6ced1c528c87d11892156d68608e521d78fe86fbe93a7dde64d54bbef2afe149300cec78b002098a344109522bca1778c560f8b2d2738a6a2ee0dae117a21f0bee034466b284e318d3a59606dfc89aa3bcc0b696b8d55eab24391f03bba66fcba51f71996708540ead932ed9f340a72ed6983245d8bffd301f0a553b6411d2adcb8fb4a21a29851c194b75dbbe826190882c38f2d24016176bec7e5b7b5813ebfca3a7372c8a120c9ad696508795775890a9184db8315f02b20177d44156ad9c6f18f1b34fe0791ebaeba6aecd32368c2cc49a906f9fd20bab73f3a1d768128dcfdcf9d773ca713e9a9b774bffb6c443316be20de7119a757350c31b7efc84b91479b264d31e5529f8148d9bcf88ec268fed34458af7eb0a2ec819e2375824d785b731bc51aafc3328f3a8324a108b090895c944d357c41fe9405e178401516a12f09bc3af84232ee789afd7bd2f82c57784f556b5cc07c371c0361057123c68e3b2d496b26778cc6751730b16ac4fb334ff434f74a5dd5826ca5583a27193ecb44426d12d011d37b8c9eda0d8d4e2c25a100c9443139be8e1c07312da2d5a852ce00d284bcc4c8aea4b91e0dd662d7e2c0d37beed49cda7455c096a67afc8bb3ba9e6426da4c537d36e0fa75e74ecb59180a0e34d2b5e044f1e011fe01f3ce3e863cacbce686d47231b1ad2906c1ac2c1f54d8b88bef37510aec086b89fc1053b418c258217f2383d772059bbea739e28a0a9e8a36333de663a3654fd8e42c6cffab37d60cfde0f64df97db109b08b21a216f7a7c1a43506bdb3c80bd1fa39e7cab059d772abf406af18cdaec4b6d22ec962e534140922c450fec2924497fc9e487eb25f90e0f5bcc8213be348ef03ccaca8cd52be9f867d53fe8e0909d46a547050ed7b64a494b4f0806446070f627f850925521b901edc613ba57e5ee734785e22c6a8054678581a68d01c526892034ae2261fd4313dd2e6969ea9dea908be42fef8c7a3e9c4bf71c177e71504032ab84023d8afc16e302de970e6be110dac20adbf9a197460cdcc0e3e7ea1560a29717d9c9148ec772677bdcd9ba76dd6e1b41f65315af915c7a44a37870ca729d03820339379955781a863bc461545a9df06bbc15110bdb4c328f61fddd0d5964ebdfb3fde5c410a7b0e3cb675a03eedbc614cc5d5cf92a5db403e0d07e9c00a1719443b1327a4adb936ec0f98d9f650a0d897fbc260a2fefbbc51ed7826a6f350ecd511e87cafbf8e731ae4327aea8f79e8b993a220c6b6127cf66f2ab347e7d4fd6f810e8e6d5ecf17189612feb9d00b097c86b99c4bb163e99402ddc3ff2e669293079a60662fbfc26e76df7adab6ff910c4dab0452e62e05a098cd11191cd4364aede266eba0157d0b193f35caa1a71ea1443a1417f947803eabf0aab269fca3dd07b6b338ba0733ea3aa93dc39e137f25e31310b3264917e7307c789d5150f8276f82ce85c569e7ba3b1987a06caebc1cb6fd75d6d125c739aed4845a8251c889b5353f1a3b1845cf03e324a82794f96f1bc61df156909765ff072c322c56a7c4bfa8911ee4fdefacca711d30a9ad2a8672a3cc9592f75a0c19f790502c6e7f9bd6fc2447d3c238410f2d27dd0f4bf1e815958dc5b6cc25373ad9850b7765accb29ab175aafb006b9b973cfafdada223c508d2bf36fb97f22e97e17a584273b5efbea43038df0427d014bae6356680847d092f1e1072c4442720c29ad2dd384bed3ca6a13a687f492f23503aaf31e5c130aab3b33d1e85f9b1cc192393fabe48c48201744c59fd211c51e5bf39788ad69569142c7b72e94d133e2e1fece7c15ba73db951742262ffb91e05e79b9f62e7a469a367c5948e8de658579f60914c522b312749b70594953cc185e18606b356a40d58563076c6df20cb25ba336d4117cc2a2a816b782f6e6f6d24d65391a27d0815f78316437e6eb98560b13e0123f001084b5e2523748c06f7e97a2369367ccf8f3369ad78f634366b6a0fbd54654d94161c83e5bc802f5b54a91684ca176ea4eafef9479d725dabc551ed597fd647859cdb4eaac2e135d96b8e633d1884985f2f6fdce07c7a6dc985ccffa1455e15cc6194ff1ec0f48bd388ba9db56c12013d3c7a3f95c946df30f1eeb8f2f25ee3ac6949130ffd40197e647b54bd9ba994bb10954a0485cc94c58b7e018d2a93513d88dbb50ec60a76086120d5a639cf38d7327e36490975be5237975dc2c00482d862a157b66e4fc41995b75f167c094e8491faa534861adf5be5c9dd73ac476517d1f36265e4db9e9c3b57a96c7e5343c87a6cabbb9167fefd5cd7b288f5f5f7eaa02433d1a802ddb6420ab2713fb2d88dbec9858b86da74d5ef07852de0ead976be62b61acee3f3e862322dafd8f18e2319769e3aa02704fa5ad7cfd3d82468ae59ae6defa9df256f26ff24400457b556352b4d7e8871ec49d09429680da37ec203aa4124460975a8c6fd1926c3688e0ff6d35a9ee3b4bdcd8a0c1037f3ccf7c96c299e1836a40e992f620d46158282909ab5ab278945fc6e36bb6f674aa8bb6087bd7d4aa901eb49394f2e4ee7a68d68d11435dc732a5350f831b7e9c13228251a777f42bd31c24c72baf79c5d27834f830f44a895a66903790c2cb44df6123f5ba519b872bee3a407514966037324564ea84fda07744e00f6ff4576c48069ba99d18887ebc84044561da5d43d6393bc1ba48ade96d8be48199641129b9704479ae28879d31a16b914324258c28e034af0ae2a0c7d4095b9a3924c28530c347b7a0a3d72e05ab16db15b47ec248e8e5459279546992ef716de08dab0857ff9d2a15d39fb68fc255f7cda8f5b685fe159d9edfc41a793a3caec4118a109e3145011dcde193c99cb9cf69a5a96955f59732e0fcee8af0e03d899527e8e1ee8768b7af6f45a2beea3982f2dd8e455377832ee1191bf065da226aa91f2965dd8a38f627fb252e91a3b2da3491db8228b8760500ea085b43ebeb6444686be26b2dbe30b11d233fcb67455a024f10ac099a28524ca592b10278e2d895801970e8f5f05958ae725d3a05a96f0b93f09737d18197202f2a3c3142d1579ccc63387ee6db7f9e28fd4046f147c8e92d1ac5891a420b41e896ed8da85522fb32aa4794dfc0143f4d47765f618a79af126601970a83deb185717c5edc2ce6a00e779022c2e743e6100a395caed3fe3ce8d0ffe86607f38d0360b5ff7438626ac3f1a6a0e5f01611e8dadbed30c5fecaac2ab612630a0e2fa7076cb8b848558fcab528c9cabc26bd415c44dcc89c1d4ac67ef6ce73cca44d34e31fb0d70cc0ae8320e3ef7f49069312904b216b607dad9faad9a1d85244110678fccb10c7d8b40cc2e3b4308cf882826cecab582286c9c0abcd15eaa6d2bfd0403fee4cd017cc94c04ea13283e145fe07bf25a7c86585f3c5fccf87cfff435af25d06f794925d778193b11c1af6666ab2792f2d948cc78fba41d2b70e458953b970023b589642316412a97563e7c6f7029d6f5f9b1054b9129b9b4f2dbf6e7d198d279dd7e92c067bffd51dc1d2dadc5ec04f2dbe105abefae97d1af09d78cc1b77b4e967c07e481d63127db71cd89e5def3bd05477aa70c80209aa0591786c8b1d3abab36f0abe172b08df64396e6e4b4129bcaf7b0b3e1b94653414c68249386a8403b4270acd0c3ad7cc9e582a25a67f3ed3b645db8591aefc8b1eca3570271d6183f63fd0143c68afc789472c439b71a2fec562c1b47adcf936ad5b0b07712f1145101f3ce546f5a7f5abe0395c761ac179283288a9c52ad5e57d1c0656129decdd5bcabded92f52967a0e11f173f84c48b3aa00b03f4a556f8594b4d08405b87d20e62ca1b3a95570376826c82b33c25fb823f9f373d0a31aa5449ca50077e13b1ab341b4aa3394082aff1bb0589e4f0ed6e4fefea882c57ff1fbe36d1d2bf9c2f72e0d24f47105cab653d7c54b48e4f756a0ca7d8a9449b8953742a07befe47ee66842d799f6cdb826e3b8d90fce73832186477d693d9851a2ac85e76072b106ed1a5fee5c6ca7182048da677b8d14b3f0e98737f5f6ff859b4e9bff995ee4d0ae91c17f5d5ece46dca6c497c8ba2d5ff0481bf39fa62b59d94b3a2f526337a8cd0af88cd632f630164c36a72047a0f6ca960c1def8f9d1a976c9c4a8f0ae4dd765ea2ec33b361d2a1426484a5ab98a85ef4c3f83aa5d7b9b5b914b62810775e29bcdeeaeca82e343f82996b3b1ebd8ebc2f86bb3644b8916c941ea7605de79a515024b3a5eea39976aa13b9d987c1c23cc9ae399fccaf6f96f64a27a08f73516ee8ef8432032b4f533b00f80777f65e1baed8e3c8f69b872fd96f555f8ee821eaeb87972beec8783beca6762559a8a73591b593172eebcb3a5698f6acae5c5e60f7290c61b837e9f9f93b8fc2c7ad0d6ef37fc2ad797662743e2b08cbc7f52b26aa85d2e086e0455e80742a3b7555a1ad19a6f2b766f97197ac500763f50cee70113b7a64c50835be6815a8839d1265e3831aa5e3b5b6031bcb24370975f6df2b40bf16be03deddc169a6429804a67a761ed5f2d5499f7e56a7202f854b472c8d8efbd044db54b07705df1cc9ea2c192ac8132291606d88689553cec7d0f9164cd66af9d5773b4523a510b5eefb9a5e626480feeb6671ef2d17510ca30085d2af7b419b358f288065b7c6b16c646d071125c0690b68ace2106d56e3d042fbf1bb7ffa4ec0bbb0f7dc18cbeb47514102c2eb38fd1f985be3254156b286770dcdcc7c3f73191a6b68a62bf5c74ec1c9b13ebc430e6b7de3f0400bd448694a" + t2.KeyMR = "bdc4d0def175d1373c4932c056f930d43ac037057da1bcf13972da31bfc669ff" + t2.Hash = "b26795a9b218fce9aec67ad453719e8b09fc850b53db398e1db208dd0494f566" + ts = append(ts, t2) + + for _, tBlock := range ts { + rawStr := tBlock.Raw + raw, err := hex.DecodeString(rawStr) + if err != nil { + t.Errorf("%v", err) + } + + f := new(DirectoryBlock) + rest, err := f.UnmarshalBinaryData(raw) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Errorf("Returned too much data - %x", rest) + } + + b, err := f.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } + + if primitives.AreBytesEqual(raw, b) == false { + t.Errorf("Marshalled bytes are not equal - %x vs %x", raw, b) + } + + //f.CalculateHashes() + + if f.DatabasePrimaryIndex().String() != tBlock.KeyMR { + t.Errorf("Wrong PrimaryIndex - %v vs %v", f.DatabasePrimaryIndex().String(), tBlock.KeyMR) + } + if f.DatabaseSecondaryIndex().String() != tBlock.Hash { + t.Errorf("Wrong SecondaryIndex - %v vs %v", f.DatabaseSecondaryIndex().String(), tBlock.Hash) + } + } +} diff --git a/common/entryBlock/eblock.go b/common/entryBlock/eblock.go index 8a785ac9c8..b0334cb1cd 100644 --- a/common/entryBlock/eblock.go +++ b/common/entryBlock/eblock.go @@ -67,25 +67,6 @@ func (c *EBlock) New() interfaces.BinaryMarshallableAndCopyable { return NewEBlock() } -func (e *EBlock) GetWelds() [][]byte { - e.Init() - var answer [][]byte - for _, entry := range e.GetEntryHashes() { - answer = append(answer, primitives.DoubleSha(append(entry.Bytes(), e.GetChainID().Bytes()...))) - } - return answer -} - -func (e *EBlock) GetWeldHashes() []interfaces.IHash { - var answer []interfaces.IHash - for _, h := range e.GetWelds() { - hash := primitives.NewZeroHash() - hash.SetBytes(h) - answer = append(answer, hash) - } - return answer -} - func (c *EBlock) GetDatabaseHeight() uint32 { return c.GetHeader().GetDBHeight() } @@ -203,15 +184,16 @@ func (e *EBlock) KeyMR() (interfaces.IHash, error) { // MarshalBinary returns the serialized binary form of the Entry Block. func (e *EBlock) MarshalBinary() ([]byte, error) { e.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) - if err := e.BuildHeader(); err != nil { + err := e.BuildHeader() + if err != nil { return nil, err } - if p, err := e.GetHeader().MarshalBinary(); err != nil { + + err = buf.PushBinaryMarshallable(e.GetHeader()) + if err != nil { return nil, err - } else { - buf.Write(p) } if p, err := e.marshalBodyBinary(); err != nil { diff --git a/common/entryBlock/eblockHeader.go b/common/entryBlock/eblockHeader.go index b459bc63b6..951e1175fa 100644 --- a/common/entryBlock/eblockHeader.go +++ b/common/entryBlock/eblockHeader.go @@ -1,7 +1,6 @@ package entryBlock import ( - "encoding/binary" "fmt" "github.com/FactomProject/factomd/common/interfaces" @@ -164,22 +163,35 @@ func (c *EBlockHeader) SetEntryCount(entryCount uint32) { // marshalHeaderBinary returns a serialized binary Entry Block Header func (e *EBlockHeader) MarshalBinary() ([]byte, error) { e.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) - buf.Write(e.ChainID.Bytes()) - buf.Write(e.BodyMR.Bytes()) - buf.Write(e.PrevKeyMR.Bytes()) - buf.Write(e.PrevFullHash.Bytes()) - - if err := binary.Write(buf, binary.BigEndian, e.EBSequence); err != nil { + err := buf.PushBinaryMarshallable(e.ChainID) + if err != nil { return nil, err } - - if err := binary.Write(buf, binary.BigEndian, e.DBHeight); err != nil { + err = buf.PushBinaryMarshallable(e.BodyMR) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.PrevKeyMR) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(e.PrevFullHash) + if err != nil { return nil, err } - if err := binary.Write(buf, binary.BigEndian, e.EntryCount); err != nil { + err = buf.PushUInt32(e.EBSequence) + if err != nil { + return nil, err + } + err = buf.PushUInt32(e.DBHeight) + if err != nil { + return nil, err + } + err = buf.PushUInt32(e.EntryCount) + if err != nil { return nil, err } @@ -187,51 +199,41 @@ func (e *EBlockHeader) MarshalBinary() ([]byte, error) { } // unmarshalHeaderBinary builds the Entry Block Header from the serialized binary. -func (e *EBlockHeader) UnmarshalBinaryData(data []byte) (newData []byte, err error) { +func (e *EBlockHeader) UnmarshalBinaryData(data []byte) ([]byte, error) { e.Init() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) - newData = data - if _, err = buf.Read(hash); err != nil { - return - } else { - e.ChainID.SetBytes(hash) + err := buf.PopBinaryMarshallable(e.ChainID) + if err != nil { + return nil, err } - - if _, err = buf.Read(hash); err != nil { - return - } else { - e.BodyMR.SetBytes(hash) + err = buf.PopBinaryMarshallable(e.BodyMR) + if err != nil { + return nil, err } - - if _, err = buf.Read(hash); err != nil { - return - } else { - e.PrevKeyMR.SetBytes(hash) + err = buf.PopBinaryMarshallable(e.PrevKeyMR) + if err != nil { + return nil, err } - - if _, err = buf.Read(hash); err != nil { - return - } else { - e.PrevFullHash.SetBytes(hash) + err = buf.PopBinaryMarshallable(e.PrevFullHash) + if err != nil { + return nil, err } - if err = binary.Read(buf, binary.BigEndian, &e.EBSequence); err != nil { - return + e.EBSequence, err = buf.PopUInt32() + if err != nil { + return nil, err } - - if err = binary.Read(buf, binary.BigEndian, &e.DBHeight); err != nil { - return + e.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err } - - if err = binary.Read(buf, binary.BigEndian, &e.EntryCount); err != nil { - return + e.EntryCount, err = buf.PopUInt32() + if err != nil { + return nil, err } - newData = buf.DeepCopyBytes() - - return + return buf.DeepCopyBytes(), nil } func (e *EBlockHeader) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryBlock/eblock_test.go b/common/entryBlock/eblock_test.go index c1075ed1c9..dd6741277e 100644 --- a/common/entryBlock/eblock_test.go +++ b/common/entryBlock/eblock_test.go @@ -186,3 +186,88 @@ func newEntryBlock() *EBlock { } return e } +func TestSameAs(t *testing.T) { + //block 1000 + eblock1kbytes, _ := hex.DecodeString("df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e6041611c693d62887530c5420a48f2ea2d6038745fc493d6b1e531232805dd2149614ef537df0c73df748b12d508b4334fe8d2832a4cd6ea24f64a3363839bd0efa46e835bfed10ded0d756d7ccafd44830cc942799fca43f2505e9d024b0a9dd3c00000221000003e800000002b24d4ee9e2184673a4d7de6fdac1288ea00b7856940341122c34bd50a662340a0000000000000000000000000000000000000000000000000000000000000009") + eb1k, err := UnmarshalEBlock(eblock1kbytes) + if err != nil { + t.Errorf("eb unmarshall failed") + } + + eblock1k1bytes, _ := hex.DecodeString("df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e604f1a3fee8d61b407ae8c43f0ef91c5bc21f9faf560ccc6167f0ae31d13c8e4628f08c42bc44c09ac26c349bef8ee80d2ffb018cfa3e769107b2413792fa9bd64200f9ce481c4e389a83461f5ebff43e10cad5d55e15d58c3afd4fc16006b9519500000222000003e900000002f3dffaa3e03b5520e5876ff1efedf16eacd69620e2d37344c73fd72e478464df0000000000000000000000000000000000000000000000000000000000000004") + eb1k1, err := UnmarshalEBlock(eblock1k1bytes) + if err != nil { + t.Errorf("eb unmarshall failed") + } + + if eb1k.IsSameAs(eb1k1) { + t.Errorf("entry block same as comparison failed") + } + + eb1kclone, err := UnmarshalEBlock(eblock1kbytes) + if err != nil { + t.Errorf("eb unmarshall failed") + } + + if !eb1k.IsSameAs(eb1kclone) { + t.Errorf("entry block same as comparison failed") + } + //fmt.Println(db1k1) +} + +type TestEBlock struct { + Raw string + KeyMR string + Hash string +} + +func TestMarshalUnmarshalStaticEBlock(t *testing.T) { + ts := []TestEBlock{} + + t1 := TestEBlock{} + t1.Raw = "06a40590f536293bdecc3d7e69a5c21785c6ed454a59caf7b2e083a1a88ac85b900986feee6603c74fc3aa925d3de2371190fee36632bd2f1389cbaa6d62a98b78fe8619ef8af7ddae3de88a5063476d04467a576c4ff40abd157429e19f1748f43a6d9767a02dbbfbec86fe962552f22c55d4f6467dc8cf019d1d0ba06a88d40000f77b0001602100000002370054e235209bda68f934c8d4bd9d84edef4c03ebd890fe7686edca138f61880000000000000000000000000000000000000000000000000000000000000008" + t1.KeyMR = "1462592f58712147b62617c6fb37380a223cd32ef673345340e94521df3c9aca" + t1.Hash = "52794022b3da85b58df69cb842b85d45cda70771677fe0011f5af852eb30e930" + ts = append(ts, t1) + + t2 := TestEBlock{} + t2.Raw = "df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e604831da78a07e01a7495719c54a23861d7d2c25a3eac0b5bfda7d7715c6937c108e9a64a371c68e89f5a3e97f6512c3864fa73448b1907de9078f5a9dc0f4b2d0aecfb6f5aee8e04bde68a5b69a4cea55e8b4e7a3a8efbf75d45a6b686874c5bbe0000000d0000001900000004c480b681b113118876e2540b1f9791af555dc2cd9b5806451305167816281710c92715fe2262b22b1b6fad0f4fc81ff7ccf5f9d633fb6815db414ec023719a72c49b069dbc664c2f247b812160c4a902826483df2b47c27dfd2a95c4281dda790000000000000000000000000000000000000000000000000000000000000003" + t2.KeyMR = "78ac31584a1e526a3739d6eac5129f6a71aefa722792f9afe8b428f34a9f673c" + t2.Hash = "8180cef1efb75d39fb581c44688a43cae6a4ebab70d7abbe9f2d8864e230e75c" + ts = append(ts, t2) + + for _, tBlock := range ts { + rawStr := tBlock.Raw + raw, err := hex.DecodeString(rawStr) + if err != nil { + t.Errorf("%v", err) + } + + f := new(EBlock) + rest, err := f.UnmarshalBinaryData(raw) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Errorf("Returned too much data - %x", rest) + } + + b, err := f.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } + + if primitives.AreBytesEqual(raw, b) == false { + t.Errorf("Marshalled bytes are not equal - %x vs %x", raw, b) + } + + //f.CalculateHashes() + + if f.DatabasePrimaryIndex().String() != tBlock.KeyMR { + t.Errorf("Wrong PrimaryIndex - %v vs %v", f.DatabasePrimaryIndex().String(), tBlock.KeyMR) + } + if f.DatabaseSecondaryIndex().String() != tBlock.Hash { + t.Errorf("Wrong SecondaryIndex - %v vs %v", f.DatabaseSecondaryIndex().String(), tBlock.Hash) + } + } +} diff --git a/common/entryBlock/entry.go b/common/entryBlock/entry.go index cb200779e2..d5ca497ab3 100644 --- a/common/entryBlock/entry.go +++ b/common/entryBlock/entry.go @@ -173,15 +173,22 @@ func (e *Entry) GetHash() interfaces.IHash { } func (e *Entry) MarshalBinary() ([]byte, error) { - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) // 1 byte Version - if err := binary.Write(buf, binary.BigEndian, e.Version); err != nil { + err := buf.PushByte(byte(e.Version)) + if err != nil { return nil, err } + if e.ChainID == nil { + e.ChainID = primitives.NewZeroHash() + } // 32 byte ChainID - buf.Write(e.ChainID.Bytes()) + err = buf.PushBinaryMarshallable(e.ChainID) + if err != nil { + return nil, err + } // ExtIDs if ext, err := e.MarshalExtIDsBinary(); err != nil { @@ -229,7 +236,8 @@ func UnmarshalEntry(data []byte) (interfaces.IEBEntry, error) { return entry, nil } -func (e *Entry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { +func (e *Entry) UnmarshalBinaryData(data []byte) ([]byte, error) { + var err error defer func() { if r := recover(); r != nil { err = fmt.Errorf("Error unmarshalling: %v", r) @@ -237,21 +245,17 @@ func (e *Entry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { }() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) // 1 byte Version - b, err := buf.ReadByte() + e.Version, err = buf.PopByte() if err != nil { return nil, err - } else { - e.Version = b } // 32 byte ChainID e.ChainID = primitives.NewZeroHash() - if _, err = buf.Read(hash); err != nil { - return nil, err - } else if err = e.ChainID.SetBytes(hash); err != nil { + err = buf.PopBinaryMarshallable(e.ChainID) + if err != nil { return nil, err } @@ -299,7 +303,7 @@ func (e *Entry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { return nil, err } - return + return nil, nil } func (e *Entry) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryCreditBlock/commitChain_test.go b/common/entryCreditBlock/commitChain_test.go index e2abdce762..c019e917f2 100644 --- a/common/entryCreditBlock/commitChain_test.go +++ b/common/entryCreditBlock/commitChain_test.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "testing" + "fmt" ed "github.com/FactomProject/ed25519" . "github.com/FactomProject/factomd/common/entryCreditBlock" "github.com/FactomProject/factomd/common/primitives" @@ -113,3 +114,51 @@ func TestCommitChainMarshalUnmarshalEmpty(t *testing.T) { t.Error("Error is nil when it shouldn't be") } } + +func TestMiscCC(t *testing.T) { + //chain commit from factom-cli get ecbheight 28556 + ccbytes, _ := hex.DecodeString("0001538b8480e3c5be4e952b9c5e711e1d5022580f1a600f24daa7302387dc547280162443524a3016ce3104cafd88c48545abbd4dd98e90d870039f436c0efd572c58371f06dcdb5884280d38c9f037139841253e256ba2fee183dfde1a6936b5773f1284fc400b9b7cfddf8f8209b10249dfc60e1cf5ff9252b1a1e0c5db178d3f616695b99b8eeaa83e3e1e0af73e47832127ed9e729649c8d17eb14f6c49db810a7d20a09cc68ff9ca017caa1fcc513c9b579f6e4d91c262aa70621de851559a1e80ab674b0a") + cc := NewCommitChain() + cc.UnmarshalBinary(ccbytes) + + expected := fmt.Sprint("db5884280d38c9f037139841253e256ba2fee183dfde1a6936b5773f1284fc40") + got := fmt.Sprint(cc.GetEntryHash()) + if expected != got { + t.Errorf("Commit Chain comparison failed - %v vs %v", expected, got) + } + + expected = fmt.Sprint("c09a488f1a070332fb51b6519d49744ec5fc4335e1ab8f7002e0fa5ce7bb4c7b") + got = fmt.Sprint(cc.Hash()) + if expected != got { + t.Errorf("Commit Chain comparison failed - %v vs %v", expected, got) + } + + expected = fmt.Sprint("2016-03-18 20:57:10") + got = cc.GetTimestamp().UTCString() + if expected != got { + t.Errorf("Commit Chain comparison failed - %v vs %v", expected, got) + } + + ccbytes_badsig, _ := hex.DecodeString("0001538b8480e3c5be4e952b9c5e711e1d5022580f1a600f24daa7302387dc547280162443524a3016ce3104cafd88c48545abbd4dd98e90d870039f436c0efd572c58371f06dcdb5884280d38c9f037139841253e256ba2fee183dfde1a6936b5773f1284fc400b9b7cfddf8f8209b10249dfc60e1cf5ff9252b1a1e0c5db178d3f616695b99b8eeaa83e3e1e0af73e47832127ed9e729649c8d17eb14f6c49db810a7d20a09cc68ff9ca017caa1fcc513c9b579f6e4d91c262aa70621de851559a1e80ab674b00") + cc_badsig := NewCommitChain() + cc_badsig.UnmarshalBinary(ccbytes_badsig) + + if nil != cc.ValidateSignatures() { + t.Errorf("Commit Chain comparison failed") + } + + if nil == cc_badsig.ValidateSignatures() { + t.Errorf("Commit Chain comparison failed") + } + + cc2 := NewCommitChain() + cc2.UnmarshalBinary(ccbytes) + + if cc.IsSameAs(cc_badsig) { + t.Errorf("Commit Chain comparison failed") + } + + if !cc.IsSameAs(cc2) { + t.Errorf("Commit Chain comparison failed") + } +} diff --git a/common/entryCreditBlock/commitEntry_test.go b/common/entryCreditBlock/commitEntry_test.go index 6b770dce92..ed807e3bb4 100644 --- a/common/entryCreditBlock/commitEntry_test.go +++ b/common/entryCreditBlock/commitEntry_test.go @@ -7,6 +7,8 @@ package entryCreditBlock_test import ( "testing" + "encoding/hex" + "fmt" . "github.com/FactomProject/factomd/common/entryCreditBlock" ) @@ -28,3 +30,95 @@ func TestUnmarshalNilCommitEntry(t *testing.T) { t.Errorf("Error is nil when it shouldn't be") } } + +func TestMiscEC(t *testing.T) { + //chain commit from factom-cli get ecbheight 28556 + ecbytes, _ := hex.DecodeString("0001538b7fe6fd249f6eed5336f91eb6b506b1f4683c0e03aa8d8c59cf54299b945d41a73b44e90117ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc38e2fc16991f2705244c83cc36e5b4ca796dbbf168601b55d6fc34187a8de061b096f3266f3f6dd986e3f2150a1b14ada29cc9c0fc3a1d1a1875f11dc6cfd0b") + ec := NewCommitEntry() + ec.UnmarshalBinary(ecbytes) + + expected := fmt.Sprint("249f6eed5336f91eb6b506b1f4683c0e03aa8d8c59cf54299b945d41a73b44e9") + got := fmt.Sprint(ec.GetEntryHash()) + if expected != got { + t.Errorf("Entry Commit comparison failed - %v vs %v", expected, got) + } + + expected = fmt.Sprint("9c406b5f2bf32f9cad3cb44b1dbcd6513d35979e6795984cc4f00e604a540c19") + got = fmt.Sprint(ec.Hash()) + if expected != got { + t.Errorf("Entry Commit comparison failed - %v vs %v", expected, got) + } + + expected = fmt.Sprint("2016-03-18 20:52:08") + got = ec.GetTimestamp().UTCString() + if expected != got { + t.Errorf("Entry Commit comparison failed - %v vs %v", expected, got) + } + + ecbytes_badsig, _ := hex.DecodeString("0001538b7fe6fd249f6eed5336f91eb6b506b1f4683c0e03aa8d8c59cf54299b945d41a73b44e90117ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc38e2fc16991f2705244c83cc36e5b4ca796dbbf168601b55d6fc34187a8de061b096f3266f3f6dd986e3f2150a1b14ada29cc9c0fc3a1d1a1875f11dc6cfd00") + ec_badsig := NewCommitEntry() + ec_badsig.UnmarshalBinary(ecbytes_badsig) + + if nil != ec.ValidateSignatures() { + t.Errorf("Entry Commit comparison failed") + } + + if nil == ec_badsig.ValidateSignatures() { + t.Errorf("Entry Commit comparison failed") + } + + cc2 := NewCommitEntry() + cc2.UnmarshalBinary(ecbytes) + + if ec.IsSameAs(ec_badsig) { + t.Errorf("Entry Commit comparison failed") + } + + if !ec.IsSameAs(cc2) { + t.Errorf("Entry Commit comparison failed") + } +} + +func TestStringEC(t *testing.T) { + //chain commit from factom-cli get ecbheight 28556 + ecbytes, _ := hex.DecodeString("0001538b7fe6fd249f6eed5336f91eb6b506b1f4683c0e03aa8d8c59cf54299b945d41a73b44e90117ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc38e2fc16991f2705244c83cc36e5b4ca796dbbf168601b55d6fc34187a8de061b096f3266f3f6dd986e3f2150a1b14ada29cc9c0fc3a1d1a1875f11dc6cfd0b") + ec := NewCommitEntry() + ec.UnmarshalBinary(ecbytes) + + got := fmt.Sprintf("%v\n", ec.String()) + expected := ` CommitEntry + Version 0 + MilliTime 01538b7fe6fd + EntryHash 249f6e + Credits 1 + ECPubKey 17ef7a + Sig c38e2f + +` + if got != expected { + t.Errorf("Entry Commit comparison failed") + } +} + +func TestCommitEntryMarshalUnmarshalStatic(t *testing.T) { + ce := NewCommitEntry() + data, _ := hex.DecodeString("0001538b7fe6fd249f6eed5336f91eb6b506b1f4683c0e03aa8d8c59cf54299b945d41a73b44e90117ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc38e2fc16991f2705244c83cc36e5b4ca796dbbf168601b55d6fc34187a8de061b096f3266f3f6dd986e3f2150a1b14ada29cc9c0fc3a1d1a1875f11dc6cfd0b") + rest, err := ce.UnmarshalBinaryData(data) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Error("Returned extra data") + } + h := ce.GetHash() + expected := "9c406b5f2bf32f9cad3cb44b1dbcd6513d35979e6795984cc4f00e604a540c19" + if h.String() != expected { + t.Errorf("Wrong hash - %v vs %v", h.String(), expected) + } + + h = ce.GetSigHash() + expected = "29be46067fa1aa19e139a9db305d46035e24c4ff1b77c58ccb66028e70e7d180" + if h.String() != expected { + t.Errorf("Wrong hash - %v vs %v", h.String(), expected) + } +} diff --git a/common/entryCreditBlock/commitchain.go b/common/entryCreditBlock/commitchain.go index 7f90337278..7625e1fcc9 100644 --- a/common/entryCreditBlock/commitchain.go +++ b/common/entryCreditBlock/commitchain.go @@ -7,12 +7,9 @@ package entryCreditBlock import ( "encoding/binary" "fmt" - "io" "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" - - ed "github.com/FactomProject/ed25519" ) const ( @@ -58,6 +55,10 @@ func (e *CommitChain) Init() { } } +//this function only checks if everything in the item is identical. +// It does not catch if the private key holder has created a malleated version +//which is functionally identical in come cases from the protocol perspective, +//but would fail comparison here func (a *CommitChain) IsSameAs(b interfaces.IECBlockEntry) bool { if a == nil || b == nil { if a == nil && b == nil { @@ -105,15 +106,15 @@ func (a *CommitChain) IsSameAs(b interfaces.IECBlockEntry) bool { func (e *CommitChain) String() string { e.Init() var out primitives.Buffer - out.WriteString(fmt.Sprintf(" %-20s\n", "CommitChain")) + out.WriteString(fmt.Sprintf(" %s\n", "CommitChain")) out.WriteString(fmt.Sprintf(" %-20s %d\n", "Version", e.Version)) - out.WriteString(fmt.Sprintf(" %-20s %x\n", "MilliTime", e.MilliTime)) + out.WriteString(fmt.Sprintf(" %-20s %s\n", "MilliTime", e.MilliTime)) out.WriteString(fmt.Sprintf(" %-20s %x\n", "ChainIDHash", e.ChainIDHash.Bytes()[:3])) out.WriteString(fmt.Sprintf(" %-20s %x\n", "Weld", e.Weld.Bytes()[:3])) out.WriteString(fmt.Sprintf(" %-20s %x\n", "EntryHash", e.EntryHash.Bytes()[:3])) - out.WriteString(fmt.Sprintf(" %-20s %x\n", "Credits", e.Credits)) + out.WriteString(fmt.Sprintf(" %-20s %d\n", "Credits", e.Credits)) out.WriteString(fmt.Sprintf(" %-20s %x\n", "ECPubKey", e.ECPubKey[:3])) - out.WriteString(fmt.Sprintf(" %-20s %d\n", "Sig", e.Sig[:3])) + out.WriteString(fmt.Sprintf(" %-20s %x\n", "Sig", e.Sig[:3])) return (string)(out.DeepCopyBytes()) } @@ -154,11 +155,11 @@ func (b *CommitChain) Interpret() string { // CommitMsg returns the binary marshaled message section of the CommitEntry // that is covered by the CommitEntry.Sig. func (c *CommitChain) CommitMsg() []byte { - p, err := c.MarshalBinary() + p, err := c.MarshalBinarySig() if err != nil { return []byte{byte(0)} } - return p[:len(p)-64-32] + return p } // Return the timestamp @@ -173,11 +174,16 @@ func (c *CommitChain) GetTimestamp() interfaces.Timestamp { func (c *CommitChain) IsValid() bool { c.Init() //double check the credits in the commit - if c.Credits < 10 || c.Version != 0 { + if c.Credits < 11 || c.Version != 0 { return false } - return ed.VerifyCanonical((*[32]byte)(c.ECPubKey), c.CommitMsg(), (*[64]byte)(c.Sig)) + //if there were no errors in processing the signature, formatting or if didn't validate + if nil == c.ValidateSignatures() { + return true + } else { + return false + } } func (c *CommitChain) GetHash() interfaces.IHash { @@ -192,27 +198,41 @@ func (c *CommitChain) GetSigHash() interfaces.IHash { func (c *CommitChain) MarshalBinarySig() ([]byte, error) { c.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) // 1 byte Version - if err := binary.Write(buf, binary.BigEndian, c.Version); err != nil { + err := buf.PushUInt8(c.Version) + if err != nil { return nil, err } // 6 byte MilliTime - buf.Write(c.MilliTime[:]) + err = buf.PushBinaryMarshallable(c.MilliTime) + if err != nil { + return nil, err + } // 32 byte double sha256 hash of the ChainID - buf.Write(c.ChainIDHash.Bytes()) + err = buf.PushBinaryMarshallable(c.ChainIDHash) + if err != nil { + return nil, err + } // 32 byte Commit Weld sha256(sha256(Entry Hash + ChainID)) - buf.Write(c.Weld.Bytes()) + err = buf.PushBinaryMarshallable(c.Weld) + if err != nil { + return nil, err + } // 32 byte Entry Hash - buf.Write(c.EntryHash.Bytes()) + err = buf.PushBinaryMarshallable(c.EntryHash) + if err != nil { + return nil, err + } // 1 byte number of Entry Credits - if err := binary.Write(buf, binary.BigEndian, c.Credits); err != nil { + err = buf.PushUInt8(c.Credits) + if err != nil { return nil, err } @@ -303,108 +323,66 @@ func (c *CommitChain) ECID() byte { return ECIDChainCommit } -func (c *CommitChain) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling CommitChain: %v", r) - } - }() +func (c *CommitChain) UnmarshalBinaryData(data []byte) ([]byte, error) { + c.Init() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) + var err error // 1 byte Version - var b byte - var p []byte - if b, err = buf.ReadByte(); err != nil { - return - } else { - c.Version = uint8(b) - } - - if buf.Len() < 6 { - err = io.EOF - return + c.Version, err = buf.PopUInt8() + if err != nil { + return nil, err } - // 6 byte MilliTime - if p = buf.Next(6); p == nil { - err = fmt.Errorf("Could not read MilliTime") - return - } else { - c.MilliTime = new(primitives.ByteSlice6) - err = c.MilliTime.UnmarshalBinary(p) - if err != nil { - return - } + c.MilliTime = new(primitives.ByteSlice6) + err = buf.PopBinaryMarshallable(c.MilliTime) + if err != nil { + return nil, err } // 32 byte ChainIDHash - if _, err = buf.Read(hash); err != nil { - return + err = buf.PopBinaryMarshallable(c.ChainIDHash) + if err != nil { + return nil, err } - c.ChainIDHash = primitives.NewHash(hash) // 32 byte Weld - if _, err = buf.Read(hash); err != nil { - return + err = buf.PopBinaryMarshallable(c.Weld) + if err != nil { + return nil, err } - c.Weld = primitives.NewHash(hash) // 32 byte Entry Hash - if _, err = buf.Read(hash); err != nil { - return + err = buf.PopBinaryMarshallable(c.EntryHash) + if err != nil { + return nil, err } - c.EntryHash = primitives.NewHash(hash) // 1 byte number of Entry Credits - if b, err = buf.ReadByte(); err != nil { - return - } else { - c.Credits = uint8(b) - } - - if buf.Len() < 32 { - err = io.EOF - return + c.Credits, err = buf.PopUInt8() + if err != nil { + return nil, err } // 32 byte Public Key - if p := buf.Next(32); p == nil { - err = fmt.Errorf("Could not read ECPubKey") - return - } else { - c.ECPubKey = new(primitives.ByteSlice32) - err = c.ECPubKey.UnmarshalBinary(p) - if err != nil { - return - } - } - - if buf.Len() < 64 { - err = io.EOF - return + c.ECPubKey = new(primitives.ByteSlice32) + err = buf.PopBinaryMarshallable(c.ECPubKey) + if err != nil { + return nil, err } - // 64 byte Signature - if p := buf.Next(64); p == nil { - err = fmt.Errorf("Could not read Sig") - return - } else { - c.Sig = new(primitives.ByteSlice64) - err = c.Sig.UnmarshalBinary(p) - if err != nil { - return - } + c.Sig = new(primitives.ByteSlice64) + err = buf.PopBinaryMarshallable(c.Sig) + if err != nil { + return nil, err } err = c.ValidateSignatures() if err != nil { - return + return nil, err } - newData = buf.DeepCopyBytes() - - return + return buf.DeepCopyBytes(), nil } func (c *CommitChain) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryCreditBlock/commitentry.go b/common/entryCreditBlock/commitentry.go index 5e0deb299b..eb98277ee4 100644 --- a/common/entryCreditBlock/commitentry.go +++ b/common/entryCreditBlock/commitentry.go @@ -7,11 +7,7 @@ package entryCreditBlock import ( "encoding/binary" "fmt" - "io" - "time" - ed "github.com/FactomProject/ed25519" - "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" ) @@ -36,6 +32,30 @@ var _ interfaces.ShortInterpretable = (*CommitEntry)(nil) var _ interfaces.IECBlockEntry = (*CommitEntry)(nil) var _ interfaces.ISignable = (*CommitEntry)(nil) +func (e *CommitEntry) Init() { + if e.MilliTime == nil { + e.MilliTime = new(primitives.ByteSlice6) + } + if e.EntryHash == nil { + e.EntryHash = primitives.NewZeroHash() + } + if e.ECPubKey == nil { + e.ECPubKey = new(primitives.ByteSlice32) + } + if e.Sig == nil { + e.Sig = new(primitives.ByteSlice64) + } + /* + if e.SigHash == nil { + e.SigHash = primitives.NewZeroHash() + } + */ +} + +//this function only checks if everything in the item is identical. +// It does not catch if the private key holder has created a malleated version +//which is functionally identical in come cases from the protocol perspective, +//but would fail comparison here func (a *CommitEntry) IsSameAs(b interfaces.IECBlockEntry) bool { if a == nil || b == nil { if a == nil && b == nil { @@ -76,13 +96,13 @@ func (a *CommitEntry) IsSameAs(b interfaces.IECBlockEntry) bool { func (e *CommitEntry) String() string { var out primitives.Buffer - out.WriteString(fmt.Sprintf(" %-20s\n", "CommitEntry")) + out.WriteString(fmt.Sprintf(" %s\n", "CommitEntry")) out.WriteString(fmt.Sprintf(" %-20s %d\n", "Version", e.Version)) - out.WriteString(fmt.Sprintf(" %-20s %x\n", "MilliTime", e.MilliTime)) + out.WriteString(fmt.Sprintf(" %-20s %s\n", "MilliTime", e.MilliTime)) out.WriteString(fmt.Sprintf(" %-20s %x\n", "EntryHash", e.EntryHash.Bytes()[:3])) - out.WriteString(fmt.Sprintf(" %-20s %x\n", "Credits", e.Credits)) + out.WriteString(fmt.Sprintf(" %-20s %d\n", "Credits", e.Credits)) out.WriteString(fmt.Sprintf(" %-20s %x\n", "ECPubKey", e.ECPubKey[:3])) - out.WriteString(fmt.Sprintf(" %-20s %d\n", "Sig", e.Sig[:3])) + out.WriteString(fmt.Sprintf(" %-20s %x\n", "Sig", e.Sig[:3])) return (string)(out.DeepCopyBytes()) } @@ -121,11 +141,11 @@ func (b *CommitEntry) Interpret() string { // CommitMsg returns the binary marshaled message section of the CommitEntry // that is covered by the CommitEntry.Sig. func (c *CommitEntry) CommitMsg() []byte { - p, err := c.MarshalBinary() + p, err := c.MarshalBinarySig() if err != nil { return []byte{byte(0)} } - return p[:len(p)-64-32] + return p } // Return the timestamp @@ -136,22 +156,18 @@ func (c *CommitEntry) GetTimestamp() interfaces.Timestamp { return primitives.NewTimestampFromMilliseconds(milli) } -// InTime checks the CommitEntry.MilliTime and returns true if the timestamp is -// whitin +/- 12 hours of the current time. -func (c *CommitEntry) InTime() bool { - now := time.Now() - sec := c.GetTimestamp().GetTimeSeconds() - t := time.Unix(sec, 0) - - return t.After(now.Add(-constants.COMMIT_TIME_WINDOW*time.Hour)) && t.Before(now.Add(constants.COMMIT_TIME_WINDOW*time.Hour)) -} - func (c *CommitEntry) IsValid() bool { //double check the credits in the commit if c.Credits < 1 || c.Version != 0 { return false } - return ed.VerifyCanonical((*[32]byte)(c.ECPubKey), c.CommitMsg(), (*[64]byte)(c.Sig)) + + //if there were no errors in processing the signature, formatting or if didn't validate + if nil == c.ValidateSignatures() { + return true + } else { + return false + } } func (c *CommitEntry) GetHash() interfaces.IHash { @@ -165,72 +181,74 @@ func (c *CommitEntry) GetSigHash() interfaces.IHash { } func (c *CommitEntry) MarshalBinarySig() ([]byte, error) { - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) // 1 byte Version - if err := binary.Write(buf, binary.BigEndian, c.Version); err != nil { + err := buf.PushUInt8(c.Version) + if err != nil { return nil, err } // 6 byte MilliTime - buf.Write(c.MilliTime[:]) + err = buf.PushBinaryMarshallable(c.MilliTime) + if err != nil { + return nil, err + } // 32 byte Entry Hash - buf.Write(c.EntryHash.Bytes()) + err = buf.PushBinaryMarshallable(c.EntryHash) + if err != nil { + return nil, err + } // 1 byte number of Entry Credits - if err := binary.Write(buf, binary.BigEndian, c.Credits); err != nil { + err = buf.PushUInt8(c.Credits) + if err != nil { return nil, err } return buf.DeepCopyBytes(), nil - } // Transaction hash of entry commit. (version through pub key hashed) func (c *CommitEntry) MarshalBinaryTransaction() ([]byte, error) { - buf := new(primitives.Buffer) - b, err := c.MarshalBinarySig() if err != nil { return nil, err } - - buf.Write(b) + buf := primitives.NewBuffer(b) // 32 byte Public Key - buf.Write(c.ECPubKey[:]) + err = buf.PushBinaryMarshallable(c.ECPubKey) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } func (c *CommitEntry) MarshalBinary() ([]byte, error) { - buf := new(primitives.Buffer) - b, err := c.MarshalBinaryTransaction() if err != nil { return nil, err } - - buf.Write(b) - - // 32 byte Public Key - //buf.Write(c.ECPubKey[:]) + buf := primitives.NewBuffer(b) // 64 byte Signature - buf.Write(c.Sig[:]) + err = buf.PushBinaryMarshallable(c.Sig) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } func (c *CommitEntry) Sign(privateKey []byte) error { + c.Init() sig, err := primitives.SignSignable(privateKey, c) if err != nil { return err } - if c.Sig == nil { - c.Sig = new(primitives.ByteSlice64) - } err = c.Sig.UnmarshalBinary(sig) if err != nil { return err @@ -239,9 +257,6 @@ func (c *CommitEntry) Sign(privateKey []byte) error { if err != nil { return err } - if c.ECPubKey == nil { - c.ECPubKey = new(primitives.ByteSlice32) - } err = c.ECPubKey.UnmarshalBinary(pub) if err != nil { return err @@ -267,92 +282,47 @@ func (c *CommitEntry) ECID() byte { return ECIDEntryCommit } -func (c *CommitEntry) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling CommitEntry: %v", r) - } - }() - +func (c *CommitEntry) UnmarshalBinaryData(data []byte) ([]byte, error) { + c.Init() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) - - var b byte - var p []byte - // 1 byte Version - if b, err = buf.ReadByte(); err != nil { - return - } else { - c.Version = uint8(b) - } + var err error - if buf.Len() < 6 { - err = io.EOF - return + c.Version, err = buf.PopUInt8() + if err != nil { + return nil, err } // 6 byte MilliTime - if p = buf.Next(6); p == nil { - err = fmt.Errorf("Could not read MilliTime") - return - } else { - c.MilliTime = new(primitives.ByteSlice6) - err = c.MilliTime.UnmarshalBinary(p) - if err != nil { - return - } + err = buf.PopBinaryMarshallable(c.MilliTime) + if err != nil { + return nil, err } // 32 byte Entry Hash - if _, err = buf.Read(hash); err != nil { - return + err = buf.PopBinaryMarshallable(c.EntryHash) + if err != nil { + return nil, err } - c.EntryHash = primitives.NewHash(hash) // 1 byte number of Entry Credits - if b, err = buf.ReadByte(); err != nil { - return - } else { - c.Credits = uint8(b) - } - - if buf.Len() < 32 { - err = io.EOF - return + c.Credits, err = buf.PopUInt8() + if err != nil { + return nil, err } // 32 byte Public Key - if p = buf.Next(32); p == nil { - err = fmt.Errorf("Could not read ECPubKey") - return - } else { - c.ECPubKey = new(primitives.ByteSlice32) - err = c.ECPubKey.UnmarshalBinary(p) - if err != nil { - return - } - } - - if buf.Len() < 64 { - err = io.EOF - return + err = buf.PopBinaryMarshallable(c.ECPubKey) + if err != nil { + return nil, err } // 64 byte Signature - if p = buf.Next(64); p == nil { - err = fmt.Errorf("Could not read Sig") - return - } else { - c.Sig = new(primitives.ByteSlice64) - err = c.Sig.UnmarshalBinary(p) - if err != nil { - return - } + err = buf.PopBinaryMarshallable(c.Sig) + if err != nil { + return nil, err } - newData = buf.DeepCopyBytes() - - return + return buf.DeepCopyBytes(), nil } func (c *CommitEntry) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryCreditBlock/ecblock.go b/common/entryCreditBlock/ecblock.go index 4660ae80e7..57cc8c99b1 100644 --- a/common/entryCreditBlock/ecblock.go +++ b/common/entryCreditBlock/ecblock.go @@ -6,9 +6,7 @@ package entryCreditBlock import ( "fmt" - "io" - "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" ) @@ -190,23 +188,29 @@ func (e *ECBlock) HeaderHash() (interfaces.IHash, error) { func (e *ECBlock) MarshalBinary() ([]byte, error) { e.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) // Header - if err := e.BuildHeader(); err != nil { + err := e.BuildHeader() + if err != nil { return nil, err } - if p, err := e.GetHeader().MarshalBinary(); err != nil { + + err = buf.PushBinaryMarshallable(e.GetHeader()) + if err != nil { return nil, err - } else { - buf.Write(p) } + x := buf.DeepCopyBytes() + buf = primitives.NewBuffer(x) // Body of ECBlockEntries - if p, err := e.marshalBodyBinary(); err != nil { + p, err := e.marshalBodyBinary() + if err != nil { + return nil, err + } + err = buf.Push(p) + if err != nil { return nil, err - } else { - buf.Write(p) } return buf.DeepCopyBytes(), nil @@ -239,29 +243,25 @@ func UnmarshalECBlock(data []byte) (interfaces.IEntryCreditBlock, error) { return block, nil } -func (e *ECBlock) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling: %v", r) - } - }() +func (e *ECBlock) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) // Unmarshal Header if e.GetHeader() == nil { e.Header = NewECBlockHeader() } - newData, err = e.GetHeader().UnmarshalBinaryData(data) + err := buf.PopBinaryMarshallable(e.GetHeader()) if err != nil { - return + return nil, err } // Unmarshal Body - newData, err = e.unmarshalBodyBinaryData(newData) + newData, err := e.unmarshalBodyBinaryData(buf.DeepCopyBytes()) if err != nil { - return + return nil, err } - return + return newData, err } func (e *ECBlock) UnmarshalBinary(data []byte) (err error) { @@ -271,88 +271,69 @@ func (e *ECBlock) UnmarshalBinary(data []byte) (err error) { func (e *ECBlock) marshalBodyBinary() ([]byte, error) { e.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) + entries := e.GetBody().GetEntries() - for _, v := range e.GetBody().GetEntries() { - p, err := v.MarshalBinary() + for _, v := range entries { + err := buf.PushByte(v.ECID()) if err != nil { - return buf.Bytes(), err + return nil, err + } + err = buf.PushBinaryMarshallable(v) + if err != nil { + return nil, err } - buf.WriteByte(v.ECID()) - buf.Write(p) } return buf.DeepCopyBytes(), nil } func (e *ECBlock) unmarshalBodyBinaryData(data []byte) ([]byte, error) { + e.Init() buf := primitives.NewBuffer(data) - var err error - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling: %v", r) - } - }() for i := uint64(0); i < e.GetHeader().GetObjectCount(); i++ { - var id byte - id, err = buf.ReadByte() + id, err := buf.PopByte() if err != nil { return nil, err } + switch id { case ECIDServerIndexNumber: s := NewServerIndexNumber() - if buf.Len() < ServerIndexNumberSize { - err = io.EOF - return nil, err - } - _, err = s.UnmarshalBinaryData(buf.Next(ServerIndexNumberSize)) + err = buf.PopBinaryMarshallable(s) if err != nil { return nil, err } e.GetBody().AddEntry(s) case ECIDMinuteNumber: m := NewMinuteNumber(0) - if buf.Len() < MinuteNumberSize { - err = io.EOF - return nil, err - } - _, err = m.UnmarshalBinaryData(buf.Next(MinuteNumberSize)) + err = buf.PopBinaryMarshallable(m) if err != nil { return nil, err } e.GetBody().AddEntry(m) case ECIDChainCommit: - if buf.Len() < CommitChainSize { - err = io.EOF - return nil, err - } c := NewCommitChain() - _, err = c.UnmarshalBinaryData(buf.Next(CommitChainSize)) + err = buf.PopBinaryMarshallable(c) if err != nil { return nil, err } e.GetBody().AddEntry(c) case ECIDEntryCommit: - if buf.Len() < CommitEntrySize { - err = io.EOF - return nil, err - } c := NewCommitEntry() - _, err = c.UnmarshalBinaryData(buf.Next(CommitEntrySize)) + err = buf.PopBinaryMarshallable(c) if err != nil { return nil, err } e.GetBody().AddEntry(c) case ECIDBalanceIncrease: c := NewIncreaseBalance() - tmp, err := c.UnmarshalBinaryData(buf.DeepCopyBytes()) + err = buf.PopBinaryMarshallable(c) if err != nil { return nil, err } e.GetBody().AddEntry(c) - buf = primitives.NewBuffer(tmp) default: err = fmt.Errorf("Unsupported ECID: %x\n", id) return nil, err @@ -391,8 +372,8 @@ func NextECBlock(prev interfaces.IEntryCreditBlock) (interfaces.IEntryCreditBloc // Handle the really unusual case of the first block. if prev == nil { - e.GetHeader().SetPrevHeaderHash(primitives.NewHash(constants.ZERO_HASH)) - e.GetHeader().SetPrevFullHash(primitives.NewHash(constants.ZERO_HASH)) + e.GetHeader().SetPrevHeaderHash(primitives.NewZeroHash()) + e.GetHeader().SetPrevFullHash(primitives.NewZeroHash()) e.GetHeader().SetDBHeight(0) } else { v, err := prev.HeaderHash() diff --git a/common/entryCreditBlock/ecblockHeader.go b/common/entryCreditBlock/ecblockHeader.go index 966f6da76e..d3041822eb 100644 --- a/common/entryCreditBlock/ecblockHeader.go +++ b/common/entryCreditBlock/ecblockHeader.go @@ -5,7 +5,6 @@ package entryCreditBlock import ( - "encoding/binary" "encoding/json" "fmt" @@ -173,106 +172,113 @@ func (e *ECBlockHeader) JSONString() (string, error) { func (e *ECBlockHeader) MarshalBinary() ([]byte, error) { e.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) // 32 byte ECChainID - buf.Write(e.GetECChainID().Bytes()) + err := buf.PushBinaryMarshallable(e.GetECChainID()) + if err != nil { + return nil, err + } // 32 byte BodyHash - buf.Write(e.GetBodyHash().Bytes()) + err = buf.PushBinaryMarshallable(e.GetBodyHash()) + if err != nil { + return nil, err + } // 32 byte Previous Header Hash - buf.Write(e.GetPrevHeaderHash().Bytes()) + err = buf.PushBinaryMarshallable(e.GetPrevHeaderHash()) + if err != nil { + return nil, err + } // 32 byte Previous Full Hash - buf.Write(e.GetPrevFullHash().Bytes()) + err = buf.PushBinaryMarshallable(e.GetPrevFullHash()) + if err != nil { + return nil, err + } // 4 byte Directory Block Height - if err := binary.Write(buf, binary.BigEndian, e.GetDBHeight()); err != nil { + err = buf.PushUInt32(e.GetDBHeight()) + if err != nil { return nil, err } // variable Header Expansion Size - if err := primitives.EncodeVarInt(buf, - uint64(len(e.GetHeaderExpansionArea()))); err != nil { + err = buf.PushVarInt(uint64(len(e.GetHeaderExpansionArea()))) + if err != nil { return nil, err } // varable byte Header Expansion Area - buf.Write(e.GetHeaderExpansionArea()) + err = buf.Push(e.GetHeaderExpansionArea()) + if err != nil { + return nil, err + } // 8 byte Object Count - if err := binary.Write(buf, binary.BigEndian, e.GetObjectCount()); err != nil { + err = buf.PushUInt64(e.GetObjectCount()) + if err != nil { return nil, err } // 8 byte size of the Body - if err := binary.Write(buf, binary.BigEndian, e.GetBodySize()); err != nil { + err = buf.PushUInt64(e.GetBodySize()) + if err != nil { return nil, err } return buf.DeepCopyBytes(), nil } -func (e *ECBlockHeader) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling: %v", r) - } - }() - +func (e *ECBlockHeader) UnmarshalBinaryData(data []byte) ([]byte, error) { + e.Init() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) - - if _, err = buf.Read(hash); err != nil { - return - } else { - if fmt.Sprintf("%x", hash) != "000000000000000000000000000000000000000000000000000000000000000c" { - err = fmt.Errorf("Invalid ChainID - %x", hash) - return - } - } - if _, err = buf.Read(hash); err != nil { - return - } else { - e.BodyHash.SetBytes(hash) + h := primitives.NewZeroHash() + err := buf.PopBinaryMarshallable(h) + if err != nil { + return nil, err } - - if _, err = buf.Read(hash); err != nil { - return - } else { - e.PrevHeaderHash.SetBytes(hash) + if h.String() != "000000000000000000000000000000000000000000000000000000000000000c" { + return nil, fmt.Errorf("Invalid ChainID - %s", h) } - if _, err = buf.Read(hash); err != nil { - return - } else { - e.PrevFullHash.SetBytes(hash) + err = buf.PopBinaryMarshallable(e.BodyHash) + if err != nil { + return nil, err + } + err = buf.PopBinaryMarshallable(e.PrevHeaderHash) + if err != nil { + return nil, err + } + err = buf.PopBinaryMarshallable(e.PrevFullHash) + if err != nil { + return nil, err } - if err = binary.Read(buf, binary.BigEndian, &e.DBHeight); err != nil { - return + e.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err } // read the Header Expansion Area - hesize, tmp := primitives.DecodeVarInt(buf.DeepCopyBytes()) - buf = primitives.NewBuffer(tmp) - e.HeaderExpansionArea = make([]byte, hesize) - if _, err = buf.Read(e.HeaderExpansionArea); err != nil { - return + hesize, err := buf.PopVarInt() + e.HeaderExpansionArea, err = buf.PopLen(int(hesize)) + if err != nil { + return nil, err } - if err = binary.Read(buf, binary.BigEndian, &e.ObjectCount); err != nil { - return + e.ObjectCount, err = buf.PopUInt64() + if err != nil { + return nil, err } - - if err = binary.Read(buf, binary.BigEndian, &e.BodySize); err != nil { - return + e.BodySize, err = buf.PopUInt64() + if err != nil { + return nil, err } - newData = buf.DeepCopyBytes() - return + return buf.DeepCopyBytes(), nil } func (e *ECBlockHeader) UnmarshalBinary(data []byte) error { diff --git a/common/entryCreditBlock/ecblockHeader_test.go b/common/entryCreditBlock/ecblockHeader_test.go index e1acf4c7e7..5de52f2867 100644 --- a/common/entryCreditBlock/ecblockHeader_test.go +++ b/common/entryCreditBlock/ecblockHeader_test.go @@ -5,9 +5,11 @@ package entryCreditBlock_test import ( + "encoding/hex" "testing" . "github.com/FactomProject/factomd/common/entryCreditBlock" + "github.com/FactomProject/factomd/common/primitives" ) func TestUnmarshalNilECBlockHeader(t *testing.T) { @@ -28,3 +30,23 @@ func TestUnmarshalNilECBlockHeader(t *testing.T) { t.Errorf("Error is nil when it shouldn't be") } } + +func TestStaticECBlockHeaderUnmarshal(t *testing.T) { + ecbh := new(ECBlockHeader) + data, _ := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000000cbb3ff38bbb90032de6965587f46dcf37551ac26e15819303057c88999b2910b4f87cfc073df0e82cdc2ed0bb992d7ea956fd32b435b099fc35f4b0696948507a66fb49a15b68a2a0ce2382e6aa6970c835497c6074bec9794ccf84bb331ad1350000000100000000000000000b0000000000000058") + rest, err := ecbh.UnmarshalBinaryData(data) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Error("Returned extra data") + } + + b, err := ecbh.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } + if primitives.AreBytesEqual(b, data) == false { + t.Errorf("Blocks are not identical - %x vs %x", data, b) + } +} diff --git a/common/entryCreditBlock/ecblock_test.go b/common/entryCreditBlock/ecblock_test.go index 86ca94ede7..43706fed5d 100644 --- a/common/entryCreditBlock/ecblock_test.go +++ b/common/entryCreditBlock/ecblock_test.go @@ -34,34 +34,63 @@ func TestUnmarshalNilECBlock(t *testing.T) { } } +type TestECBlock struct { + Raw string + KeyMR string + Hash string +} + func TestStaticECBlockUnmarshal(t *testing.T) { - ecb := NewECBlock() - data, _ := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000000cbb3ff38bbb90032de6965587f46dcf37551ac26e15819303057c88999b2910b4f87cfc073df0e82cdc2ed0bb992d7ea956fd32b435b099fc35f4b0696948507a66fb49a15b68a2a0ce2382e6aa6970c835497c6074bec9794ccf84bb331ad1350000000100000000000000000b0000000000000058000001020103010401050106010701080417ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc3d09d10693eb867e2bd0a503746df370403c9451ae91a363046f2a68529c2fd00822c0109010a") - rest, err := ecb.UnmarshalBinaryData(data) - if err != nil { - t.Errorf("%v", err) - } - if len(rest) > 0 { - t.Error("Returned extra data") - } - h, err := ecb.HeaderHash() - if err != nil { - t.Errorf("%v", err) - } - expected := "c96a851d95db6d58cbcfdd63a8aaf93fc180fb8c003af5508667cc44fa31457d" - if h.String() != expected { - t.Errorf("Wrong hash - %v vs %v", h.String(), expected) - } + ts := []TestECBlock{} + + t1 := TestECBlock{} + t1.Raw = "000000000000000000000000000000000000000000000000000000000000000cbb3ff38bbb90032de6965587f46dcf37551ac26e15819303057c88999b2910b4f87cfc073df0e82cdc2ed0bb992d7ea956fd32b435b099fc35f4b0696948507a66fb49a15b68a2a0ce2382e6aa6970c835497c6074bec9794ccf84bb331ad1350000000100000000000000000b0000000000000058000001020103010401050106010701080417ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7dc3d09d10693eb867e2bd0a503746df370403c9451ae91a363046f2a68529c2fd00822c0109010a" + t1.KeyMR = "c96a851d95db6d58cbcfdd63a8aaf93fc180fb8c003af5508667cc44fa31457d" + t1.Hash = "1eb3121d81cd8676f20c5fec2f4e0d7a892a2ab2f086506bf55735756098d9ba" + ts = append(ts, t1) + + t2 := TestECBlock{} + t2.Raw = "000000000000000000000000000000000000000000000000000000000000000cc6f4356eaad3872e1d244d93ba24f4ac37646a5ee23ce0f3bd005be5c33ef13a7a6b88a22c16f752b50a3ff52c9ed8201e7b96c7a2a856e520190a173331c92df833729fe02ab9a2b45d4ad6130c05122c558f8fac0c8d3b1e33b9256fc51f3e0001602100000000000000012e0000000000009c580300015c3ebcd1b4ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f726a90b1e65c4000d6eed85cc981f6320ee74490551a3de7c024016f0eda65684d58e2a218c4c2b665bc537719d53c9125a2213e00a4c2c67e39f841a0959e0c0300015c3ebcd2e6ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f673dc43ecfb64542a5f5252670a6026c20c9e008d8730e3934de47c9ff5a6c89c50c2fa035e2d83724c8f9fd9f407b10c9e6a65b7dc9ce6b983a97b817df95080300015c3ebcd3183e6b4935cf2929297b91802bbce6f145a342ad8852f8c1f823bb8aaf460c567102c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077ffa04c7c1c69d9925d0a119a2e6327bda0afb9efb54363f952aefbcc141fdb7494a80969839e74c8b61914fb5ff718745174a9d2cdbf8db6c000103ceed462d0b01010300015c3ebdc209ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f28de2ff5a4ebf6d6e0d2ce25a56ccd29d5afbbc28ad2894338aff1131578d9e2e6d7330d42129fb1e3150420d5c2466cbd9721e0057b8fbb7be2315de6d9f90e0300015c3ebdc31b81c045fa49e3c0891e3d06d5054aa37ad644c0e88e3e73f8b079bcb7083033e202c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f0a0b55e987447d8ebc4da71a7beddd47b73d594eeb0081e0aa75414329db7feb1ae2219006190f1dc18a0a3b606ddb66df5af37ae7df2a97b7cf894c7aa944030300015c3ebdc21d7bc6391fa0221a9b08f67b6e63810e230e76e6523782eec10c1aea21a17648c601d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de003d167d76def6280489984b2462b88f925799d3d90afe79e18d7835760c7504950c2f617a54c8f8f0ead0a0b85951df9362b5e9ac3bc132312bc57fd65a95b50a0300015c3ebdc311ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077ff1f853f060a652ddcb0bd16d007367db7fcbb83b41b82ee1d6ee7c474febaf54ac404b953217587c087d55acf4f744aa013c22a333a5e4ad3d454678388b22080300015c3ebe9a537a3e18e3d082da29e23cb010e0fd8e46a7131c57a2c6540925f0f9327e58a47501db98e60e039940400704d5207842a00dd6968b1969fc97b7227b7e4a8ab565aa21cb333fe61108da9d3373793ef5ed990557e0a97eb627e048e53d631058b836bdd6f67ad369a47a1e6f621e8f06f35ca1dfa04f67f557a8e125c5f06fe1a80701020300015c3ebea730099c4f0b33ac83553c446ab8326e95467466d0b4f0740b38b5dfb7263f31d75a01d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de00850f946403cfb2513747aa32f1ab3cb38c72aa1d0a086ec2fb2954042d118674d7a8b841f1b3f352245f2aeb6bfb586d1f266eca3d720bca9454c533641c6c080300015c3ebea833ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fa9db7780c95baf2f548e9919e4506e6529bf836d587347a029cd3a620047162040a414924852c430603aea18f0f79a22a9859e9c8b30f4f72b3fbf248bcb900b0300015c3ebea906ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f9046e2c856e8eae6ece903864ef6581e2b12a72e25826fd531048bef5bd369596fd429b677b5abcfd0437acba6f02aa790c6094f0df8b17906b4a0819b38c00d0300015c3ebea91a7073de27cf2d846c34f74a81e0f2206fd06fd2e9fb6dac11363c09684e41396102c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f077b44e0466662266383ee893921d0e4b378d9ffb43a69f57b8d8e0911c5a5ec26b0fe5039d6eff2d8fdb9032b78871c8380a234966787ac579ef2ee9d95650e01030300015c3ebf932c2e023adbdb35a6bee4ea701cb86d2eab6033256e7dbb6a79edbfb21cb78ffa5901d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de0064ade527e1d6a648d4d7bbf963de8e1f6971ac4c43dc6e49da75d2d075968677ae2fdbab6a02899a0d533ba9560aa9867015fc68de444e9454447875dc44dc0f0300015c3ebf9426ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f86f53d6aedfe51eded202db6d34c5939867f3ec7e8190db6adf7d0c59304830a3cb567767e1a1c12927adb56b35aed84cf55517a73b9d8ab64ddd45040934e000300015c3ebf95ebd48074ddd63224e7fb833489f2555e22ca944ebc7ddee70a0b67c5283cf49a9302c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f96dea6b3b3ec2c9ccb477d5172e52f07c85cce38b0db33784c7462e17de900e5f89b04997b3a093cc423463b05441d7cedb46345419eb9b6769e1e1e9da9de0f0300015c3ebf95d3ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fa83dfc7fa4181e3370c19a18c159844880417a3a71cbeca2f2209e09fd10e62f3c38de8c06d5a9423ab1a9ada579065509b58dd3cef0a81d9f7a32e91f3290010300015c3ebf96ff61f120c0cc6f9fb82faa1d74c548f43290546fc0249ef843af91ac792e34575901993e57f755ee402ff5df248841f7a621f6d2c08f0616c5ca5fed983d102db610faf19c97f9759cb590eee68155f0a5643372cb4d351a7133e6d2e9caaba96a614175184bcd1e14925dc0313fb488716b3411c54ce52e3371e9bfdddba91bd20b0300015c3ebfa184c7bac1c7b1395c98fa92d468736d5a2fe4542758396492b3bd6af3286ab2cf6d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596baaac2c56a93f76bca6139f94b064cbcaa21ab709ed7636ce93c637939b76678b4ee44fa0efefd76ccc0adfb6e3f26702f66f96b539642ff640718771280ad0e0300015c3ebfa55c7c4bffbb5c63cdc06f94f0a7eaca64a6a7f1d3964208aa8645450cc025aa053702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ff8462dd0d4a655d3c5c5779c1bf414046cbe90760ae30ed43dccfa1470de5fce2a3a32d37a76e591a34cdbe150756373d671c6a999de7837eb416e9c726d0050300015c3ebfa9d6bcbe3876779b4bd6e41ce00cab0bcb34d9d062ecb830f3db96943f8ebe967fc901e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596430d85dce498ab8227895ede1b20431ff318958f50950bb11e61b4be6c2bd259416627feeab5b11a02e625d1cc3ffa9910e46461201d6a87cbd853f0555db6060300015c3ebfad5a6cd4d8409dc60c3637eeb5bac46d0023bf015d1a59da1de8b7eea4816cc5c70e01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596986863080bf39b098549e3c6a68f556a37dad85a8625e2ab542a72c4db4d4c51628443062d6f23225e924bb5c4e5a0be7994aa0cf6372032e38fe73b700ca60d0300015c3ebfb295beaa491a44a8226a84de917ccb540859baba372593b416113c27cb151371bd2b02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b4c30e31f9909d12f8bfdad05392ee760f5aed64acfcd4b9ea0ad60c8cb1be037b11aad25baf726eea9462349641d4828524da810a716ea83a6b73ca4eec640e0300015c3ebfb6cbc2f1c08a52013a7761f85ed2ce3992b91044600c08a46b49a93dcda50f8d554c01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86eb071c2af0083e74218710634eb12f9b4d681564af738bc86651dd2123105a95eba47a7814dd83d39bc2e6f1c521d70bc04a60ee0ccc9b8b907c99a2abee110c0300015c3ebfb78742a1580ba22b1573a02eeb42e790654adaa48b23a04801f4394d9745e884730e02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596eba940c1f482efe7eef53f395969f30c286b29973fe20ce1d82bdb384eb67103481136e1ce35ec462bebf6f07e554df6b2f69956c1b05dd0e97668958034d30f0300015c3ebfbaf0054a48ef4ab2cae489fb4e308dcbde8222fc5b715b3826620df4c209f3c8af5701c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf860a529f8a5b458af96ef08d543edeca65baaf1af2ee69b22285ad7119fd263290bc98c2e325dc01026af4bbd29a83a2d50e4114a1312f17e960ef5a207577d90a0300015c3ebfbc5c1d50f066e4967229122db526f98c909e9939e262ac8cf836b13727417c626bdb01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596200dd3eb32ea7e34bfa830f4aac3eadec00a0ed22f1d0c42fc6b905ece18958e1530f2d24dd40199fe776d8be86cf5c31afb966b2fab402615d7f74c7c9856020300015c3ebfbf06a36b6954c3496d09e2027a719fc60a889286efbac9aff0ae8cf962dc8b068c5201c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86b5e201ccda1319c2ee35c7e007c059fc82330ee3f24f1c290ec882bc0efbd2dfa70a9107166de55053ab0a50e9d18c37a8393692b29a95893ba1f43e8627c20f0300015c3ebfbfc1cceb8baf8d8747da38940aa1ca3daa967d5640e5d6ecea0de7380e20e911041a01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259646ea70f5e9f14ce9ed99a9b6ef2d0274267be4f263dbf5a2676182e0f379ac79b69e4b14d5939ca269e77a7b238dae84fa8b59887d48c702d12b089382cd37060300015c3ebfc3184f7c3629de8228dafd804327e8a15acf8973168fdeacc4d5d79b7d8bb0c9b87801c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf860d5b4959b9013e075374c97815365dedad488bad24de14d507e835a253b5537b0433ab26e7207e0f84e1cff3d46f76df93c5c6fcae22a158da845324c0fceb090300015c3ebfc3a341119e8fe3414ab958bc09668c19c33576e114a2befab150827d6253974d752102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961ee5a41241c34b872484ef625f1c9e27afda8c81977dcbb4a53797d4632290869da12c0b54a4b163b5a488cce53d39b286f2e97580584006600a5174b3dea80b0300015c3ebfc6e180f557301c63ea6100d4d2026a1f419d7309c51a004be642776d578babbe9a4f01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86381331be61383af2ec0761c2aa4a41d567898b5f6c15431ca0540ff8d1fd93cecb82dcacc073eff9059387fe4d953b2fe609a28122429f31135a4b15cb831b030300015c3ebfc8e8b1cc4bd89d2c19109ac42e5e5c87bb220e17fc357b9a1a6f311e639ef2e025c102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962c1c0eef5f1914682bc6abc8019d9f2024692ec96eccc9e8960849a6d58a6d952f5262c188eb986a0f17e18e5362e446e0b4fadb6bfc2922fffe56409c8d9b080300015c3ebfcaf476c4bcbe51dc929c77be3c90d202ce621742ce9f94b84a50d97bfcbf91a213c101c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86788741ed67dd8ad44c59653324fa31fb92898aa3d07566d8eb51940718903ee38e046d0c3e5e78e31e42ed1e549ff0fa07152050d40f26744962a8584f07990d0300015c3ebfcdca3b5c9a4c585794fd95f7d039d44b62e64ae4e6419b40c8abc3b76a88af9ecd0d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325966122129d096b664abbe2dc03b9246e2d4778ece17d61df05542f6730d30653fdb67561a9622198d0ea4da4bdb97a55d8dc0313d6481d464c4a374f4dcf54ff020300015c3ebfcf096fca520c7a10bc81922330fc7dd9c4f6f098b28be456562a153124b23d21bc7006c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf868daedd7dc7e363017942cb14f3e06422c6879c8fc442eb3482d566f94587db6994b1a2a349572605aac59f9d3474044b2a2bd9cb971d51e14e732ec40f25020e0300015c3ebfd33335a94183de50eb3102855bf378458df940e3d76a5a9931aa5163005c3aa14d8b01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86c85ea1000ca64a68e468bbd07d44e513eadfaa94c2440fece6e65992a2274a4e86e8539ef2cd0fc2eee953900872edc95d58a39a27fad5dc1102ad802b5ed60a0300015c3ebfd7490727fa5cf19e59d849affeb5dbd614c27d9ec34118f12d58003cb54de673fc1d01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf861439b0ff8a6e3292a3cb1e0fb373cee0281d54c20c8b6338add5347fc790b393fcee0fc57207cbc9987d4cae420924fe3ef51039e83d7a43d912d792e899360f0300015c3ebfdb068bb7992856fabd3f356334e0a448938b7e3c3870b7be427a6159c0b018b2d7fc01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86cf458968628489596156ee4d3469c118ff1634c3b6af9733ceab646b4c25190f94701be055f87d1dcbc7541c08f15a1437a2e6e135e54a847fbbc0293f0c550d0300015c3ebfdf272413d4c42d7f9d07d6305ff0bf47ff5cf14666e88fc3d30ec06cb05e186ddbc201c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf863099079f1abaa888882e6444bbe47e5c24c857575c073eb491b31441ef493fbcb6329e655e29781943118681c7263284a71d3b78049d812c540c1071dcb0b7070300015c3ebfe341dfb67603423e7b572314342fa053f1c22e7e8f90f587f66a497c8a2e960322ba01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf862fd6fd06f765f480f84d3e14aa18d45092f708626a2fb9d66359b3fc7294cb7082183a79389793c7fa2f0676ed7de97edc6348123a0d49b5a484cfd0fa65900c0300015c3ebfe75517b35594698df45e9cf5c0b21d4fd8444951daba99f662a5569f406e147a989901c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86325c79dd259453bd50f76e9ac90dcc1efef47c71a6d6a7d382ef3adb6013a001245cb57635d5747b1918b61b0029af40a6545a8dd6d0e9655769c9ee3a9ad60f0300015c3ebfeb83747d8c932cde2446f492e2031d75c82675226881584f32897ce898b206ceacc501c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf864e492439e39fdc515681d9b46ec035479e7b569e8090ec44871c4c5a25550f3b61a618d4e0d6a5ef1ab099fdd26d625d57d4bf275d3613ad40c686e05139b4090300015c3ebfef5c34918e67fe3d41f768ce184240712f8d3a57b6116b339ffceb7bae229037af5b01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86fe57e36bfe33b7a4e3cb9c21d48c82c28f98d44ba9b6991c7d59c17fb7dfd1f71985b35a825bedabcc3c5bc380a95391ba2871117234ae694c4cbf5824e4bd080300015c3ebff37f621800f0dfb137f670b331e7f4e7f3694e10814ff93873ff3e13b0a1a58bdc6507c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86298e0c76725ccfb8e2d0bf8dddf760e155fdf31235710635fd9a98941737e1fafd7dcf7092cb5bd0454d1fc4e1db36d686d0c3dab56aef31064062ace8e61b040300015c3ebff7ac3e113dadbe84974007669a42264f63b508db771186e3e03a5a73bbb3cdba759701c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86c4d7ff3350f4223a8cf5ebbad2a7fe8c69c28c4f645fe284ef1e1cde9e13f6dae3245c67ba3a3af7b6d8ea2dbd09fc1e9834c8116e54b1ee4cc53274b0b1860a0300015c3ebffbcd254c0845da393e03aadbaccd3a8bdb4defaa53219d54f5cf32b6c0318435f32701c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf863bc46cf1f7064cca537e1ad30431155a963a85e0ad673365bf0a7bf3d53384e3b075bbae11f27ea669bed784122464502e29f3e488148e9247faec60ec20a7020300015c3ebfffedf23e6982a837802bd7c9f3f2d0b12263d715d2ec5ef54361ccfa5aeeab07c98801c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86298862ab3d0733300f26bc7c28de5aab2ad9c30bc2a0b4b46e06b59d59b57e6d4e06f686cbe46a24a181aa298ada7668f6a6ba94bcd4c1292c961ad0cb6fd60d0300015c3ec001a322f844d4454aa85ccfc70cd680f266ab7fca3f53acf1846f805e1ffe121601e901e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969547210734bc3ec923d14a380d0af8726848a6785784580a381f4770f2b47a01cae9790b559087df6e22c164fc9567dc7bc2050ceecd981b9a5bd81619c6de0c0300015c3ec003b5f17bfc4d2078ffcce464513ba07ea8fd95d0c3397144a8f43b3887f4a7a39bf301c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86c04c692da914908f480d58156aa67e8a411626f61fe69362e2cf453bfcad6089bf226d94c5cfe8cd11f4d7013465484dd56caa12ac78973936182cae30dfcb0f0300015c3ec005c3a7142e66f584c27513d34a7e1efca8d0592cbbab2d74988a77a57e25682ad2f101e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596454f191acb815d9f4d88ddf04f5654e2ba70d03d21151d8dc515a71c11df77c70d5f12d05445d9d2ccf82169e3517f3a0a9d0c4d5810166f8a3645db3eefd5020300015c3ec007e5e893eef7d7c2b459059e4cbcd8906aa64b984644febd9447265588dc1fbb936e01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86e1b149958c38bf101073a04068989b786bd9a64cc77fa37b60eea781c3497f77dbe06ead786d1f948ff073fcd2243d698ca9c63fdb10658acdda7e404edcc9050300015c3ec00ad340f3ac1f115bf3057979d6491de5de2ddf049e920bd9aec8e0fd315b87422bd202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596a8efaef24f3569e928139351e8b6d91bf8235307fb0535a8f9202793569a3644656fa70dc69be288162840abb8dbb8d994868df25b1d088162d89cbe44997d040300015c3ec00c07a5ee6d0b015a321f4bc0799f92a634abac32d3e40a383bade967cdf4aa71aaf401c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf8672d09f478f8167b2e149723b5fc08b7861981925c56bf572143e8c225e8d96cd102ba4e149edf78b60a67b97c3d28c3927af8c48513bb8a4fa5459f9ad8ef1090300015c3ec00fa434e601853adbcf0e3a76f65a0ada351a984ca924b0965e11b278099199c609e702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259606711312ed59c3283bb20aa667764e5a1f8bb462492ea664fc517b611b06665a1192fbb00224b380319e35da65c451a28daa5737cdab8771b6e3eb494783a6090300015c3ec01407e4ea7f6dda1503b46be4bb40c2d690f14d94aa55f1b3f979e96640430da8612802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d131dd8c313be1dcf2b38b6a445da700db611536088700ea58d25c11aacdc16ae29fe25f74d6ebe640aed838f78a88e01d3253492f2db8e5e870e7786acfc10f0300015c3ec0184ad98cd729428353dbd3465f8a84adace6ed6cc350c829d50ac9caf9e4ffa265d201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e30ad31a39da5aa16be723f6874321f3ca1a97e26280873764e3b904843faa5c0df3291303d10736fc70ae263e4333622cc1458c779d9afb70dbdb9a21d5c80f0300015c3ec01d49ba80a21f76cbecbe43e85368a1f22f0a9ddee19eb3a405efb96118bc520133dd01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259661da724d4124eb572970eef5928b6e01eebc3665d166d5c70657a2ecb13c1be806fad1215565a34ed27c617c7e0a0e131be05d126e2279ffcf3ef35f379e98060300015c3ec02158d890095f48885ccfddb16575e2275c4f4b33186229b6ae51f16921e582483bb501e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596dffd237a78141fe3248c301f597b2d580a24ef7c97d009a1f8c37e4382846f7cd1abb39d01dc4f89ba7046608337cbe8ee8d21268a66435a7e63c9719a0dc9060300015c3ec0254742cce6390aac7a6a1dec9df75b13df1bc15f8009dfe594c931e07871b1d6349a01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d9a3f2c31e482aa4e23d719fe64e4b401758181c1d8142c70fe9fdc7b82387276582a83f94824471e7e5900feddbb062ead0998ccb3148b0f6d37c00d5002e050300015c3ec02a8454239978d2d9becf50ade14e38247b1ab501fa2e3823d5f64efe289ad5e0539602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d179e338834072dd1509b453fcfcf254a977da7c6f3636f66e010a1042e57c60eb7be55d4aa7ff1df5bc8a91467831e26f1c1c2b4587e2ef1c93cb616cb4e2030300015c3ec02fa7ada5f52f46a5ecfb9c0d2d33eb9d35d740611ccd31fa3ed4daaec67255d1e69601e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969b9fa13d0926cabd5d00307436f1ee35d09b4f62cb26ca5995774f80862e60b57e68ffd9524dc823117ed7f5e4f4ffb6dbc2087869fbad8e0ff7a3127751b5010300015c3ec03542bf5ef59eaf92a09ecc842138a409cc724fcaf5a5dab8cdbfcd93b668814d0a2002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596bd2f9388a6d18ad7aec07a6eaba200cd2508fe758caf84cc7e57c3bae6b52925ed714e7f1844e1d7a9d24ef793b0e6c9585ed55b03f1099fc079962e04c7580a0300015c3ec03679b3ab6db69b6cf22a10071728331aedd6b4b3ef973a2e943434be6fb4c25adcfa01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86f3d1393950caad196280703030a346b330536c1b1554051d1e4b99686ee22477426b859f231a627f5a0b5684dd9efa3b87c4469c65d9f33867d310ce4af513020300015c3ec03a5e131d1ca0ec1ae7158d8264d55923204d300d6619f7a596f8965e120ab97f7ab002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f09592189c712e3fef472668d8ebafe33426336241369e71a89054f94958174783bc912ed81242fad2e3f70f6d72f2dcad197840e0145d1ab8ac8b7f14392e020300015c3ec03c4c5697fe08534c3191a778f80a7c23b82a0914e45afa61fec2c17ad65c99aa887c01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86ad105fefea617ee9144ced99c52aea5f2e546fbe5088d0f0463937a5460a67809ddeb2df1a1b175a428750bde3e39a7dade3ff1b470d2ef742e0b30287bf650c0300015c3ec03f68a89f5abbc6b5b8d33f0c0a6b1e9f5bb6908088a235b3f1c8d3aa5366e006da2f02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259618b38995cdf8d83dcb22dbeed6c2a4cba8f6adf41b14170aa6f38bd8153fa19231205a3a77971bf066bfa5dddc0341299d951accf6f72b1d684c5405bd8e67080300015c3ec0406b3333d6325431fae1d51cb408902816f60c53d0d24393fa37b09669f4dfd3fde001c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86385ba680225f9d129d3c57d575070c9ef3bfa9ccbb73febe3be342e568d0536335983125d7bbb6b4331c71190db618ba119ff89a92634a42593ae261ee325b010300015c3ec04389fd85bc13ffce8cf872f1dab6f46a7b51972b2a63194aaa4177384b49d4b7025101e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964ee4a8c987c5adeede550840515c6986b3e2d1ac1565f280419bb98d4054dcdd4c69279430f4460c48d04c53c2c409b4a841610f6019a62e52b600b2314b89090300015c3ec0447d622c6af641e7b21e687fa2c8ce10e66f084c3cd8d709522f1cab4160a77250ee01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86ea64e56f2750576e7af9a9a32134771a5671d8431b76278e0112eacf3c1fd18d9e8a63d50884c07e3f17a953eccfa84ede3bf52631246d7e67e44b6b58dc220b0300015c3ec0484ac4959c58a89037c5bcab055e25728a311cc1c78ca71e97c0dad56a941f340be802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ebaa69a86f45819739ea5694edccf2631780daa52c5fc6f25545ad2ce0f15e638efb684ffb1161a5885fd2cef06b8652cfc3827a13e94874f400a7ca8c8ce90e0300015c3ec0489fb38f9b7797db4f5bb300fdeca1f7e0a450e112029a2f5a635718a223b8760d8d01c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86ec784ae30d7a842c197f264878059815d7945c62b99e0eefed724b2bde1daf038c54017bbc7e61d03b22838e1ce238ae0556f4e3fdb872f6cc81fbb7f86ab80a0300015c3ec04cc0d934bfcca47952930329249a2b1acb3314670c571a4b0263fcfe8b370f426fa101c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86d8414763de7544c12a48b9c2853120a20c5dc0cfc754c8ea92eb8899c9be0cf1773804be9cfc420246f348ed9a78fb0d14a3ca657a007d0df3cd6593d4b26e0c0300015c3ec04d7362a5cd2d22fdf62b118fe129f5e0d85a21f7857901e0ddb5bf364c223f2c87d902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962bebe6c33ac0cf40efb981648af5014242745b76a35725ddf754ec7c776f6bed69d554bd5f6b832ddc2a536baf829da76bcf244891d42efab5039f270166ae050300015c3ec050773d7fad5768b5db3a1e949defdd1ac2fbb87aa39c1b863c53004c09bf7128362301c878656b554012d9d539f3250b28898fad8d299b59a292711194a0f810d9cf86f571f3518559b2cbee735ea03a7bf3ad22a82d3a18728e919202f90d836c4fe038c365451297d2d7d8e9851af7c5be234e7855905a5a2174338a26d4278ae4070300015c3ec051b2cf1acfbcfbfc2dfa579ef8622682c02f867721fce8f93c026f5533bdf031cb2d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259679b6672c77fe9c6659651e610f5bbaef59be9276223cb3aeb85ad85c6f87c14aa7e0fe2f9e4486cedcf1a5f9584d51aef5029d578fda881fab1da06ff39432040300015c3ec05660a08fe311a4699480c79ed0ea9c447659b0a7984e8448ccf941578a974bbc0cec01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259684e44c0144556fa59ff9532d82795283d679b2aac8aff2169ad28f50adce3b75107e1308a468f923c1c03bb6aa323c30bc7c62914643af4faa59fda7e5608e0c0300015c3ec05b162fa8a98f588ef0aa89547254edb021a8318343fcecc82f742154b404c38b173701e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596582e2c126e6b2d8eb5714ab15ac4d168f3d813986594b1a5109045d191f3613d60bc1dfed4f3edc5de86ed4a706bc12f8e2ec2b5bc20a29942f254c0dc0da6080300015c3ec06027d1513202553d7e343f4d33e9361fbc8b6af7bec080948741fc4373fd9728183102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596249470f8ffc9a5dcdc1cf2864eafda03cf74cb695f8f375f5fb5cd259077601aebc8753a12012429d77aee3f93a1c092652497b44b0ad47ce5de4d58f522d80f0300015c3ec06584c6e1bd02f5b0901be4d7c6c7990cb2ac2c248336b13678d582579c92654acdea01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965fa8c23bcc57a987e08c0c5bf490e54913f65f731e039a9bf3b44451c4af321cf2217b7905f199c50d82a7adb8e68523c86de199bda549763901bc38da0723050300015c3ec06a778fb9c6788ae622c5ebf52ac31564732cc9b4f33c2e34777d99d5b89e83ab926c02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596302ac94ec3e345a86808e3c5c75bb4816afa3c7d07402f53cecb6623a84f1142a2c2acb641d74df1b383e2a14750ddfe539e5e9c01b564a162d3f6aeb8364d070300015c3ec06f294ee8e078e788acb0028c0310ce45a8a2b1458212b4d6ae35b43cb5c3cc16116f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968ce414edfadbfdebb1221dfb02d0dd62d8ab106413a706a1ba1a57e9a2c55f8e228b3045919e8a9610eb707b0b137e9ad1d68b1b8157c1b38a1d8a2937c8a8020300015c3ec073cf08eb8d55a18c4296eb262444aae9cd4ee826be64609e016396ea465b743c18c802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968f21ebe7e1bc06b0c4f8ae6d52a401fe7a05bd7b8e027c35e0a09d445c1b6005cec957436ca061d4ddb37abfef2632539e6dfabd39cd8a7b15ac2ee627706e0801040300015c3ebe8877694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596364c369ee3549ce3b9c816f6c276aa38f5c3e1978fec8fee532cc0ff66766b3b8be4db03b518fd576f046edafabf2de0669205e73c3bf0232828ac5b774822050300015c3ebe5c1a694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963ae04ace5b54c055bfa408a8119daa23a40fb791944c7e0a03577a1d28e803da8aad690f5ac5238163f4d91dddf63da9ecd5c3347ee304b6fd7d888ba61f51030300015c3ebfd7ee694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596dee00a4e5a6f747061cb1300dbe835f35e064ce9f291c2c9a688fd2422e357d0f8c249de9473c533d0436b38710478891c36c464795f733fe21b2ab4fda6f9090300015c3ebe2e94694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962b718ebf5718e5a2a790094b8882913cd14c4edcbfc405e828d0dc8250ce04ff0462ca852c8b4e49fa9039cfd1594f24f05b03ce462696df3d4e5f142528130c0300015c3ebee87a694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259654c29822e6c7700b1305df6714857ddc40aa908bb9f478db157bf2d55b290509901c08be54064d25d0927c5248443ec94d336b31eeadf2bd6f51facd2661160d0300015c3ebc092e694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259615294d0b856ee6962998ee8ea6b8d1e33bc22d1afee8450bb21f8d58b6df5e98a1285ffa7277410f7ac8b428f3d2fe8dcf0a321d9be50943403ca16d7262320c0300015c3ebc9b8b694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e18cf9528cebd93953ece88cd1a13a0b542964dc54d062db303fa81340c919b87878d0a96b7d396f028e23afdea4a5677e1cbd355c5a9f8c27d65ffcb75925010300015c3ebccd29694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f5d28d6759c6e1e05e4ec335239771598279a11dee1e023b3a51d9d842df9753d4d53564441c18737051daf4993f4c359ef1c55a0a65277af984867575ba39060300015c3ec0377a694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ab9fd2ec264dfdedbc55bafceb0c9e02f78fd7037acc1b16c0f60393ac6e95f065892f91f945bd256e199fc29e72f213adca03956169b0e664cc3bf11559450e0300015c3ec0670d694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965f33cdcc2dfe0f5dcad34517b2dd89946c997344b6be27ee7f3b616e93c092ad691d4a5681344e7b3d7fbf7887bea5ee16804f3839f591cbc4faa91347d668080300015c3ebdfc8a694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325967b816e1a85ec76506710934a9716210e084fe7c8d1d5e318230da217e853baaf4c7a93a8d48d1b46f2fecceaf2019e026df6d4494acadb595894b60cdc60b4030300015c3ebd2fb5694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963837eb7cdb66fac81f7b6a6f491759f7b35a7fd85b1b0b5d70ae1a267db74cb405082c6818e7553d9306f69674da19559d69ae5520708b1d0a8035630c26a2000300015c3ebdca36694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e4bcc1380490df080b419344aa563512c871188194d4904efaf2005d836ef0f0b5e46e934d329a6f39facb62c19564d6b938a288ef5738561df98151ad83b90e0300015c3ebfa967694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f0afa844c8b402929b7c06c02659e926895bc8fc6df0866013762cc34896c1ac091a479b688b1767aff1968542c9db267dc742ec6ff5271321d9affefbf5fa0e0300015c3ebf15fb694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964fa952eb3f0ca1d74305552d7500ecd37b79f6d7b0a8a5c0b88a36c9964402bcff0f04da505485e2e997b241a34276e88b5817cae07b4b44c87924d2ed2be4070300015c3ec00747694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596db77cad1ba3a270452cc82e02af22deae69147b3b6aab7b8822bc9f6cfad7ecb30a2ce8f0723044716b4cec1256f36d6512a858aa848a47bed021313035b00060300015c3ebf4778694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259695f69ba2273023aabe16f43675195488c2ce5a4cc2b3e65b23c0cef1c19fa5462906b5db9463eaeb9fc95e4ee5a2b4fc75c7b90ecbf371141d0c9df6bce817050300015c3ebf7794694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f0434ba8beac676fccf41c41f46e33c4cb54b5c12bd08dd09c4c29dfeff0dc7d3de5b0f5db790feeedb05f25e3d63a721f187dede699fe2c0d2c9339ab193a010300015c3ebc39bb694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e34f111a5eb60e523be7bf0c889e8f87399579c420ea5641558294edafffbadc7113261eee87445c2c3f2c5b5eaef37023246bf6bc60194f8be338db51cc70070300015c3ebd98d6694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596de6afcfc787c7aa938a1dd550d7201ae4398c1b521896c2aa90ae7b1a94719bb1c61776a8da747a80d3dbe9f7b7a8b3d1e4b89a6908ff03fa5704723957a0e030300015c3ebcffa7694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969b86a57109d9faa0039ede9b5eb06f5523dfc5e858f62f3a4f36963e90aa176d6d7d7715277ee4d5fe32c13e3957b7e4d8c221f8c6b069001bfa1f491cfa66050300015c3ebc6a73694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ddc39c4c2b59d27f926b54d2ecb85cb243942d11df0e4e6098192d94770b4866f81df66afae2f783960c67e38582e39e9dd72a16ea89d95e1e5bd6c90972b1080300015c3ebd6479694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325966999bea8e9259fe5ffb6dcfc0822bf3461697d69cbb1184d243054b39ddf7b42889e76b70a319162c59ef56d303f625f04b455c486ed3dd686afa0c16c041b0a0300015c3ebeb9dd694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259630e991ecffc93bbd50b6f554a4375d2452de166a8100c451efb8affe7a7d0a5818ecb72c30a11bf5f7b0c9f30120b167b1978e583ff6506ffc941df32348d4080300015c3ec0781699d9f79dcd20c434f8758a7e528cc9bed84d29994657112ac3e12b983769fe3f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962f60aefdbc57e0e81de94496bc49308240f002f2d3065a1ba8d3f1b5e416f7135a24a91fdbb1ba79af0a3c1f86391ef50d762954175026e7c537de58fde6df0c0300015c3ec07b4931b9b72b0cfb6566583857fafb7968367fae377afc8bf2442fa64797c1c4003601d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de00811e06ca332ac073e3856bd46513e8ffc1789e006bb80064b9298dd14ae8a16e798e04c9c02c0984ed710028b301687ce709a5de613cfca4dd1e4a418fffd20b0300015c3ec07ae4694bc4c8fa578ade5227c405a7a5f2ac9832efc58f48d298021014ce7eb210ea02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259699a2be82b75361574a8e46a34a9984a3f454f03ca2ecf134e4dd0d61d105b38ea5df75f644ca00d0a382d80208264cc544a7dc98b17b7c643ede023f7c722b070300015c3ec07c60ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f03633396a1224773b3c1e60745150a8f3f5e5f777d0f7eb1d9df53b15860c20eb5432c63f545e4ae26f73b0b4e9b6304ac48bf5d1de1d7e5e6ec12f79ba5a30e0300015c3ec07b50ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f748126a1beca98a93e0e0225e797467c20d4d872ab64ff65efd18ea733e8e2b20dce74de09173584b68480f19747960e626227c56b81a45a7381223c192c680b0300015c3ec07c6a24001d17c2da35e61b0b7ea29013c341690a0b594ba84bd777ababf2889bfd0c02c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f4ccdef2ff5b451cca4fe9d4390f21208ce952263bafd32f93a613e493a004a5367d4420fdc66e495bd4b40c46afee89b068844b447167c4a19ff5eb56fb708050300015c3ec07e288fe7ecfe8adeab94c6b6e5a94179d92bf6399594eabca20fe87e1bbed9546aec02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259635ff5b4cbbec792cdafed7463e2d47942763c2fc18751ea59fc8787cd09090f6852c02a63bda6fc8c3c4409f025a19d9b6764cde4df3cd7b595b5f0d89493e040300015c3ec0835cbaa3ce8d8e84788dfdfcb3d659e4b2abb1e0fbb081703bb8cb2186bda1bd908402e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ae13b16f7c7c7ad91109c7a99d74bfcdde37e79ca17156df78687b87e6999f6e0be17952dd576312c8583791edfbe59c2e1bb938df28c8ecb855a6af029bd9010300015c3ec087f9e8b5411830b91d4bc9c23a66517a6a29a3c2aca79fc30e887fc563d2dff8f0c302e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259685ac85eda4855332d7ec04f7c7d6691d4a02782c192320119b39ed480877ef7893e6c665e20cfe6d21424ff0a1aa9672095d1df76fef1be678d132434e7045000300015c3ec08cc74bdadcc7e83315369d31f172dbb2a25d5c916463f4029a81712c69411d23f95302e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b477d21d8584021162a8bc332f470bf35fb39377d63c99f1b88dbb7b3ad044d196b45599ae900cf22a24dabf7d4cc9aedaad072864d0cc69ede750a11188d5000300015c3ec091e386636fffadcdeeb4f297ed195a38b2a186471be6a31c7d362744fa56a2fbee4202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325960443d953b336ccf79ad1911687271997582c31041fc7b29a8d0ef44de4fec43e2b120aa7ef0e4d37787ba5f504f8cd36d01c401fb231c1b0a194aa932efe47030300015c3ec096d56d581673622ee285c1c43416752501d12ee49542a39400f1f87d3f587836290b02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596be3cd65a16f89308f0392fcbb2f4aea65714f17cb46fefe123f69c6992f2c504e91ae3a54f7ab9266127bdbde34119862c1fd0608dab104bbd01703ce2894d070300015c3ec09ae24e345fe05e0dd2555b4cab2f8a36db0fb61943818541f856c9259b6b1676012301e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ce188070f5d7243d49b146b94b992201c10a96445e8021bcbd9f3adb62dcbcfb9004910169a00156eb93312c1e98ca7e208e8371518dd495fd44be26fedce5050300015c3ec09f6c5f6d534adebecc4df2a95b619a41485e13978ee149e25ea9dad1f6a3dc0fff4202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e843b60d234a010b5508ac2cf0a4245e37a428b2931e11635a272ded047eba874706438995a8c3948e69495992b074ec2f56f5e05df3e6397baa24c312def40d0300015c3ec0a3a19634ac3fa5ff1638824a75dbe41901a06f247ce00d20f0f26ba64d7dfb5d29ec02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259660335e4512d688a78327b7e37e4d09ab5bbd914db86cfac7bafc74a4d07deb1bdecd030764769178002803569e42e90a2365b18c292e119b6c7c6568476931050300015c3ec0a8ae8bafc2657b5f3be4bb282c9c920a28acf60d4efbe58573770f1090b0de16428d01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325967d1385c59f51f35268d1ed533530253a8cb42e9e2b07f76b51e83bd5b32e5a62dab83289463d32f5b6e4ad13f253657dfeed1611425feb0dc66854100ad5dd070300015c3ec0ac72d193e9cb58076c905a213b22f540c8eacf25b5ecdae363135b0cf6b3039e0e4802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259608910637bc53ebf088062945caa6677ca440e842e4e758cae763f8c02640063a6630224834f1810e3d09e44a8c54c3560a4ae37457594d1452b0843e36254f060300015c3ec0b1027b68ee5352080d7d47ddd0d2de67c38f5b7eab12389fe619bb42e756baafbf3402e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d898b9f98674eab6e4967745d86e88e7d6cc4a9862bd733f30a280de3a0e98126fed86f89807ac5a6f0ba55ea7a9bf1313ac00db048fa7b5f9288f91a1c3310e0300015c3ec0b5536e7ade224db5b94f1f97baf6bad2c97c0aa4e1dc7eed5cabe03e831c50ab63ee02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259695447e7d01bb43897007637369b0995147ab7098a47749b95d2cb481558e01bc693b8a0cd7521e825cb6fcc3231a47099386ee804368b192bcefd47ca6b173000300015c3ec0b9aef4022b8c457261cf8e883370708425d226055859ece2ed8ef4b5cb142d385e6a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f69d845589f337349898f4924b392728abd014a6b8112a5898397e1c767d2bcbf2e263579182ea4666fb58249eeddea54edc427a0b7cd880c8f4e19ecb9b2e040300015c3ec0bdfcd1c7feba8aaac5f116d94e092744cb45e0a2a10d1fc5cd64f3228cd20fd89ed502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965a2ba1571890aec2263cedc3a1ff3a82882457f9361c03ba89a25af430e4e36131187e3973eaff9c1c588513ed778e73669ab60bf9fd2d3259d84d5e7959540f0300015c3ec0c135d212d14a95e0c96ceba2f30b4b3de83f2312d0230f256aae3b7674e58d2251d502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259652cbbce1a654db401a29bf077b87a14eb226f78ceef3044febf595ecc11bdf9696a4019d323f9598bbe9cb26356834c2057cdae0b93738ec91eecd26a44b2e010300015c3ec0c5b8b021575a220577e08504c8349aabce1e2c19460ac7614600fc816a7571c09e4401e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ba0586c380238995c9867925b64c0baa77173cd92c5484f0ac16ec24eb4b5175a0774ef31d76e506c42f4e7a4853d7d47e1ea79633016664a1f5136e198157090300015c3ec0c9f179228da93025e653dcfe57471212651c5bede6922cb5ed0220298ad48eea288802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962f58d5b8b251a10386a5218d18fae15f9404819a6720c80792b799093146c3353d4fe5ecf58077fa825aab809afe520c8fb6bca535a04e31cab9ab6d055a4e000300015c3ec0ce2fb3bcef474e30270b32480e67f242852c4c1183042060ea8a50797baeb9667e9f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596067d06a28ef77287b16d3a92ceea80a0db64a6161b5030fd2eb17b4ed1fd4a4726526463b18e41cd7efc7a8fa7c6d9e013d214f0db2f61be009e26725ff053040300015c3ec0d2a7960030d18373eae0be9ceb3c5aa02d1ff5ebfcf7a10fbff8e792ecefa50b23e502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968e13bcae452a8f18c47510fcb3e8ab40792c0be555a8d4a1c5cfdb99cfc1ba67960f57c3644ce2b501ca32000ff3076512d8cbadf5750f70619a674bc656f5080300015c3ec0d750836e5af9ed54c1ec072e8051a56c68029a66df46f1aa52e765adab3ce3af255201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ca770359842867dd22c117b55ac72e409f9083213df09f3772dde520eaca8de881150abc81500b175eb20f187ed48941c92ac30a1cf78094e8419e68d174b2080300015c3ec0db59c067b228f34e46abc36b4546a27a542a99952968659a86de65d39ceed7689a9402e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e3e7a6e1e24f0c8bfacdcee8fff08bf085baa6e234c8b4a673900d4e0740ecd1d8b78f0b6517cd5731df2474cd3421cf4a4d53c6c67b3a9e104cf5935b8b98040300015c3ec0e00ef0635cd97c53a18f0b353da7243352a8ace4d0d9fcbed08efa12735e68e3172502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962a6fdc660fd6e43e77305de954ac379a0d2d372729aeac6b54cdd0a4d087eda7c4890fd6de68449165ff03e1895f6c9ca3ce02fa3bacc87fda0459c9252d25090300015c3ec0e3c7890f01164ed43a665daa8ca0d2e2302d79e07c8f78d2b9eee4f93701fb3ab40102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b6537182521d7a586502f228059c431640a57813d46a6a9636bbd4fd1546efd890ae155cb440230bf5b0d6bc1266090682186aaf9af3c1bc786baa01ba3c1e080300015c3ec0e9168b4bbeae7380a48fd101b6ec65343cc05af9d81938db287b27c91393d6af48df02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596c95963a94396334675710b1c36d8d0179640826a59386433acf292368b0e34aab031b6c264da86246d351ad9945aab6cd38fb6ced2b09a9c5c797897f4658b0a0300015c3ec0ecfec2e1cbd4cef64f77943c5976c86c9c6d34a9a6acd83c30ec5d625dc889b08ff902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596a927a9723c94913488150ed4d86d373e63a6dfbd2439c8586cbe7b26843ec6b3249f834ab2d38ecaa65ade0e3c6ab3746a78bbe41b6f249d366371e8e6f98f0b0300015c3ec0f013354983c05598f23c2f1d52dcab116b44d3f3a0b5fd96b37e6289a71955ed63f701e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596afe63f7a1bbe68e4f84d05b70a1df3d2749f42cea7b957f1f37073797a7f31c0a6bd7821aeb09d8311fb6aa39e82cca102dc778ac7a4efb2e1c4b9178aa8f8040300015c3ec0f4ac089b559b687b48a99b89aee6458ff9a7b999cf3805e23c83b702bfc339cb60ee02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596df2766c6397e6b7c07dcd84a05bb15b40b848c85c60d41e8c482ba42653143369301da74aaf74b2d0a05e0df934cd0e94a83dd753a482d3bc63e0b7f2b98b6030300015c3ec0fab8c8b430b39d18a2324be650b61eceba26e45a33d3d3d9dfc5404fbbfa7a48826801e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969073076d0427b19cf05b24014bb04c71b0b9dda779a2c97fecf18431c2249125654ff183fee885754e5c6dd746cd6ecdf4bcfb3e088c41553b0cb1380c3b9a0e0300015c3ec0ff39b8b11f2370d635a8a7e2ca513d88ad03fc19d8fb83dee52eda31b82498498e6201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964ab7c4e498245413e7ddd3a3faf329d75905b7a341af17c02d60243aa96d738168c5e91c8ea6aeea3be2d096e55a4391a1034750973be7c2b9b75cf0dcb302090300015c3ec105b51f8d55327b6296cbfe23319d374638d2dfe3974717ab79120e4d564b419bdd5701e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f76c655c02b43ea686adc2bc8859e7f187ef4ab83e3a5668105e3d85b368e1d37afdde8cb782d6d0a38fd1a7541d9ecf480fbdeb5382e3f385befce13e01cb0f0300015c3ec10d8c0d60a03ed3761e352d80b68662562eb4cb80fc304e0bb70376db58f952b0a2cd01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962128f06242c7dbb4ef553f0626f4725581eb2105d65adf2bc5f10d6a3e861396767e3fca4fbae27c3642fcc5d85ade87bd5f14fe2e2421faa54144c6ab78d20d0300015c3ec113a1096a4afde111a2261bba70594ee179307201a3fe99850b8f4cdb624b7351b4e002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596968ace6439e16ed467b787e34795ec2e19fdef7f3d91eb87f2b1757baa754e548dd112cbc1f133290c93ec04e8180742676117f50ff624a2efd09eee7ec44b0b0300015c3ec11823851be7d6a8653628629631d3a126f3a97019f6eeace9d2ca8186b2fe9f02a8d301e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b808b2439749ff144bde46b3ec81f723db3737a2b462f66fc5271274ce31ea1c68801ff8a4586b7e105e2d41b5f92240102e87f3431d5d79fc105f714fa504090300015c3ec11cf4922a9506507406ad32465e4171381be267e92fb51517d2fe560e1d6773385ce701e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964a9013a2442af6af269f8bc9aa569223ae0dc3e354a372bc18610cad361067968f9995b0a4a5c9297a06e788318eec40105a304cf0515af272b9758eda86d0030300015c3ec1242888f835fc35e698921e6cc09a0cca58fb45dc4896fa5ecc08658081a208fb9a9a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325960aeb2fcdd3a893ebebaa19594d08ddb7d41cac6327d6ddad4f1fd220cebb7b5050a623e4c22b55a156f012f658fe84494f57ceaaf23b74d8411e07e03f3eac0c0300015c3ec12956e1b28644a88d2325c8a80b0374a4155c4bf00c5096159f01dcc3472e151b85ff02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596727e53e0b848c8787123c273fba0c01f8e06506fb37f3fdf22f7f212125b548f8b30545d4615bde180c3d7bfe76e90c78dab044fd3077dde10769d50143bb90c0300015c3ec12eee55f130239c8cce02f0a67c6f0bb05e98d958ffe121e0332ef440dbf11d7172c201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968b6c911dc04dd55d5968eea501766a86fbf1c11a5c757a80d4fdf0d6bfc210ef22dc51b05bb7f3629fc06c952b12edf9dfcecff1153809ce572725ceb2705d0d0300015c3ec132660bd4caee64b7f4741d22a55358072331a747f11549262b50d563c270e49bf57201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596838ee5b50e6be92085a02df815e0ab0ee084ac3d871a1f8efc7da5f3c5a7de8c91e1136c0777458494173e707955077ab02c4605b1be168fe278fe0478c247060300015c3ec137b136831b24ad6f8cbd3b8f3dd33e28dc025a7069215a897f29c9029cdc05f2f2cf02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962d0239bb3b6726a592478f285f4037adac5738bdd25ccdad5eb22d98cbbb0a0cc875bd1407396d5cfa8a750d3688dc36380b47da1350d8182d9beb80066c7c010300015c3ec13d22926b529d62abb62c87fe910361e83b40da8e5f082798b532c192d00235e8407f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969eee8d7757dbb31e8c114f8a44c624e554bb649bb3e619107274522432f23203fbb7d6cb273f7359ca7f8cf9c5fb26a19c56aba988f93526f3cad0d18a4c790b0300015c3ec141d8c029a37de96fe371c381dff6c7244361476faac0934eb7f286bba81ab649186f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596c12bd78527c02f237e4a18941157c65dae6cb113f6452ad41e7faf31a5c042fffe11937c620db69c389c533cc2a7ffd3d93811aa34a1cd577d5aed2f495e19040300015c3ec145eb7404cb8fcb6ed31cd9d11d96d8f132c8a73c7a5ffb617b8ece07b73cc0d24d9401e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961385b622390b8b38eb297e5c625fd7ce14d07add318097dfc739b7176857b880a232fd56ee760e4bebc426a481ecf0ff4ffe3e45089e09a10df771994243c00a0300015c3ec14abb8468e2299b3ee0115139946097e72a714e7bbaf6318c58b07f6f044baec62f0502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259620a0a5fbcba97ef55b4be39e7b58e59a9ce4b654775cbe81afe0ca6aa61a417135761885dff365800a424f79b13ecd13a1518b6e37adc53f0761e5243738860a0300015c3ec14f4440209dac5e884f8e517a427a006f69bf4da36cdb8717aef201b2b788bcf6651802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e46101a08f7b5f097fd24bf2c2a1fc9c577fd14bb5f1cd2ba38e06a4109f4c2e906b66bf5da6748493cdbdafde136007a062f2ff27e8e71e37317b603086ce030300015c3ec1534546c6ea8286281388f0d237b0e1a3e19ead4e594fac674123a8eb472b7bcc2c0202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f5658ac592ea44e30549ec2f06ad11748c036e8b688a25efcc69ab2f32125084bbbf0d7d82cc7ddfff1a868f12ec6e88cf0bd8bbdedc1bc976adab218d1d35030300015c3ec1578de604950a64b8bfcb5d30108b79c45a65806c14767b96140210255607079f769f01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259624d2e70a8ad5d7eb7ae52dfaa935722b499e841329508c6937e3dc8291a5b8e303ea6a4eadcf37ae0100304dbdcf50aa60f7875fb53a34de040175179094e2070300015c3ec15c5a7a0b8ba1384dcd9f9cf02cbdb066e5883ce657f49e0c2795a771ba739ef9b0bc01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f745a130caf4b441e46c248875f6b815bd89a7651883480621aa8077942261cace6105dd90edecf348b2fcfd28acd0d59f1f546adf05033d07da8f5a9fe7d80601050300015c3ec1613803384f2748a6538d9d2753dce2726fb5588a2533d6c41a4f160f94a624c25d1a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596a30e71c97883132978d614224c0b31f56d99c995f1e147a7e2a99b7671502db381de3cd7292102bb53a802c4e35552ab142d16f45f47759c2a9f8141182cc0040300015c3ec16553ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f082d8fc3fc5c5b67321c2ed69741494e95519b71ad74c4bf1cb9f6bbeb33737c03ac11443bd5325242cd1a3301d64066e10dc5b2d2675b88fba0519a1cb2140f0300015c3ec166868a9b05fae6b80c09d5322a7b35b5c865cb783d4a869fba05df9fff9522c2734402c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f1615e0065e0272a485cff483746572a6fcbc71b43d59d216fdd4033139a079b00887cf54686fbdeb086cfe5ea9fd14e4db7a41cd69ed811aee36ed3f790ae50f0300015c3ec166a7ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fffab443db0739b4cccbd3afa0e9213c5356a6771be6feb602212b81591ff33dea30ef2b2a0be5af4b5a362c9f6fcaa0a3b3a50fae24d0187e880defd6ec87d0d0300015c3ec165d5d05c40b76d2335f16ea3ecc1a94ce641c86ffd8de6428871352c262d3cae4f4701d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de0080e9d1d7a07b1e1791fd42f25b4cb978a9ff6e494b619dea1847e69b6bb008f460bdeb94bba73f7f276f1a2da10b654bd59a2766636e24e7aae7e7fb2c12a9000300015c3ec167d7581adda5d7953a052d5bd598e188f1a0cf18366fc44e8b1e553a5468c355444e01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b6b2e50e80167f5d6cbd36dd0e2be8ea09d67a31b53ec67cde683ca2b5455fc7e252ca64d5b87d1c1e039f638647faaf7c396418d002db43c8319609c2e7df020300015c3ec16c6c229784b3998244d3569ea5e719bf1dc62954c34fa802f3eed296dc8a45c0cad502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e4be175b5b59a6b9c48fa3dd63fdb999c3ae7f651ffb30fe19302bfd66ca5488bb34311285a59f134dc73120b329de634d2d3af9ec407325b8c790218f8df00f0300015c3ec1713357735efa986371a3b882719048707c23f4dd0f48188493284c2c72f18bc2b4dc01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596786f676c1d287617e97d8e0ada590cad76de6f1ecb318e52aa62846865641fe7e2b8b94858026d8e9d896aeeda2282d126107a997f0d1be5a680194c0068da090300015c3ec175f02c129fdd0cf7c237ad65d46fedf985033259c35bb61bd87eb4abe56714f898f802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596ae04bc11cfe1bf1c5241d68201a07c5411019dcbedb499e2b29f99b65868f5bc825e076e291652ab0c6385aaf76123191a1e5e367437084756c721be92acc10b0300015c3ec17ad43f37feb75333f745ecbacba89825d0d3367079987ca6f7a20b89da4ed80777e702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961840356ac32ac9fa05c7eadc14af10abf5d18c9532a0f671167fc27eccef73f93d66257afaa9e83238f415d7e7570e0ca0628065076abf45d2aa770695a9f2000300015c3ec17ed3149bcf70339a16a023e54be4d737d64cdac48aa9dec9b663bd791e5af7588f2802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962c903ced38149d0294217c38831ef762897472e1beb51a10bfdc6ce4f37a2ff8f5fee3b47b3835f1110d2af405ca09829a6ada3c2145fcd8bd7064ecfb52cc0f0300015c3ec1840b40a91c065d3dea964fe76658517df60711831ed7dd9a6da390307af1222d2bc901e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964fb2e717fd49eba79c2f07c6c6c3de76f29b5a47a3ce8db6483757f3bf62b83d01ca30e1f5ca1dff39fdd412fabed3cc69c6314731bc75eef71c9c531e47820b0300015c3ec188605c8170c9091ba3a79fe03f4d419fbc3cd08106168257533809230cff0c9b784302e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e9d66e3c95bff9306bed3e1448a40026ec487cee284c509c0b26ca6692dae9e7f963c239aefe46691ce8b4be9457604ffe7669bb4975df66a31e11bff226c6020300015c3ec18d54ddf3abb9daf83d77f3ccd9c17aacc00c92a086df6bb42a3519680a75ac8f188902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596525539fb0d5df98b5437880c64a782c4a0eef31085a745c687d7901c0c5596dbbb07eeae863229f936ae8d17ca0ade8c7f373c8202f6fa4fd3f3b814f1cc6b010300015c3ec191b017048da55e4beff1991914f7f1e4c1749271244cf29a8068cb6518996788d66d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965c1cb6a8a2237e0c9bd816949b399121c074ff71c1e8dbcb15672a45fd98bc4d1b76e0e3ca95ed1f1869496b55ce03c20378f65f82bbaa75fc317100b80443000300015c3ec1967b963549fdc9b1d3f43270db539a68824bd98905fa1c212d7bdd7fe2876c0cb39502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259623e6a847b9b165fdde0d09430504dcab31c1152cf87735a706ad0dcede620c6aa879b64d7dc79450518b2c2a3a9308e111b88958997fecf62f7a5b1d3b87b0030300015c3ec19bb5c69b09fb22e2515575a6e96b638372eff2a46a02b06e1be8d5ae4f34967fe1ab02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961adb3f9988861cc671e3c4f981657f5de6b149e798a37b3e816f1a8d49e764aa1fe6a9667768aa806807b3a0318701fa3bd56231e608503b2640da12d58b280e0300015c3ec1a052076e9374fb20eceaeaf0c9a1ffe39b32137b63e7b1d75a1a375779c1cfa50e8601e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596bf97b305f7e3754ca60433b56c2049812cc120d9094dedaec5fe79c36e4ca86e6383a528bed55c8d1cb3ebb7262568cca69e49e0916fceaf18d75e50504c84010300015c3ec1a5446c143315f1d9a74cf4f96659224120997b65dd6b0206c749bee4d84b2f374e8302e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965daad6128c0f1011444659cff14e6f12068d63d6ba0b34646a59f420d9ab4bd59ca2e63a585ed671000ee0fb04ee7cb27a394180a4231d02f155c4d327c66b0a0300015c3ec1a96bb91e60b7c470f3dae702d90016ba384b16b8c792e8162257c1494a8aac7b35d602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259620b2bc911cea1ed9e2b1cd68a9a9dbedeb0a71dbcbcf3ae026c07f0122ac5aea63ceaaf75e3d59a5e7c65694cec9fd3b3f082754c5f462162b610031756f90060300015c3ec1ae1139f05158484720453e4d11eba02560524dc8e28be8c4791e3d4b68b26224063702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259603b8cb857e45cd35a22defc5e307a6dc6741b77bc8828293baba3ef83c7c4f11e8a7aeaae61d75a1221b5f570995de5c97f353920a752def4d5fe59bb25c14060300015c3ec1b2a2b4baf83172932eafc945346f745addb39eeb1db3acaa4263071f416ecfd7672e01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259666efe60025626d485a68ee113296776a1acfb0338b4f42e2c68ad2856a8a297f9830aca01104f114b2b6e5f59bdaa56ca42da42f1b6074f127f7d4e0d0b1430f0300015c3ec1b7e8138f928ce32ed23a02515ce1d6d1c1f4ff109189e2f3f5a40ebff98d3dc495d802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596deff1c6548b630d612d5c7a334a7738992f7c29d7d00ac27f783c2f3bd011ff5a23c15dd0d09a84053fdb10b8a133c0dcb4aa079da1ef41ac9645f6941ccdb050300015c3ec1bcac4daa5d23e5b5b4b12bd7c1b87eddc3588854c0ab938040e50ad19625e575cefa01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968face77fc562f7aefe73b0fd1307751d5b0ea9833a09060de696423ec5f9ac73e91622f5596a2cd37f51f79bf162c1daa8fdf45e0edeacf90537afb79861600f0300015c3ec1c1f05c0085f2b757245600d22c8be40b3e8f1e62373ffff5f1258222a120ddcd370502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596564562a4ea205e0c7b1a288c70e2d55c5eb31de78f528bd47aecf50734c7a1cb3d7f376ad638a97cc3860278dd85bc9cbae9ccd98a2de0d3612c28cecfb9ae0d0300015c3ec1d08859aeef5335715bf200913ca58a56882b65e28588a5ca828ccd5abe79d6e44cbf02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f72085f50f38931865cbee979f6672c9f10516da89e7d7c413b0207eb3ca71d510d4a03934fe8a3488f7650309b30f8c34cfc9d18271690701941c204b1050070300015c3ec1d4a3ff45c4d3b366da61a74eaf99f886ffbe82ea0a0fea9ca48ef8403f8edc38fa9702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259653aebe5fe80234a02478a8b052152e1a347749a06b69a8acec4061ec0a4eca691dbcdec65b4863e589463192a06704a82d5268abfa6abf4c5e841977400e3e030300015c3ec1d93a1f73238ffa9b2bef7b1e310fc1c4aa19e1774b6a6f8dd03a0a79aa888e04be8a01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596662cea5bae78756d775e4e157c51d1b61872d8841290b0898b4c6499cfe8fa5942b993d7d19f2efa94f0443b69b56289dce8dd4f742e964b556278299c4bfa040300015c3ec1de24c0eee3561341cbdaac9f71e8a006fb4ccaf2553529dd12dd5e98364bc098109802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259678a7985d316d0b8f21cd76f45718a7f068a61df112bc0c859972f5c5cb99dda3b1886d165736edb499199c3358998fc798620e03a58d149762a597623e8e840a0300015c3ec1e375e8d203f24ae4da86a4bd23c3bbbe185dbb606b18c9e7e2093649a6678bad434602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969cc4832d39887dd53f8cbbd49a3df17756ff83905f8959f1cce4699d87d30cc788a9c54c55ccc3c10c2c1625c244f6d3abeb0605092ddc4ac64347e179beab030300015c3ec1e7824d5eff6060a9b156f5c738c563dc93334927fb04cc52c03f852c0fb38b7be53d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596651803e24c61e3a48f77d434bc71f9f01418ef5fb44b4169b00f1a1725e3cfbcde924825aeab468d4bee0d0a18d98d4fe698880cab053851a637d5f099f07c0c0300015c3ec1ec36030c8305ff35d4404ccb37ef27f24956bda0e6e927a865ec5798a698fc0de37a01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596640699b06a8e9245adf04c517fdb0d55048175a09630f7ac855c62901be6bf5f7f5529f7177a10330afb954eb93bd5d1599f75faebe225f27e67f7d9d99159050300015c3ec1f0cb2c12cd039e51b9db66bdab098eba8972a9d9d1f89556c45f413c80e1768d312e02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259685e8788e3183bc8ab3cbbbc7fc59ad9b4ee5c131287b430583f2161d8999ef8a5760feb82245493fbd3efec1690fcbb6e48726f36cc86e26bd4b775d241b15060300015c3ec1f594b6297bc10f751ea9fd1e966105ef1f56e419eafc2eef86980ce7ec167ccb1b0302e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964ca81254f90f817b4b9357aa05f1317558700035b35f1970eba84a44a2774a8d399a481c62d08f95d9cf1b75d29f9461775781a8f83c72d6518cb648d75b3f0f0300015c3ec1fa643c2345e7961ad8eea983a0a16c7a47ea43bf37c9ec0a7f42120452080b1b3e8802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e8a2143c015968fa44746b3fd6cb779edf4b0819eb2de42176eff693c8546f5ed36a6d6eb331ff27bf5fd3795b48cf9590885243a6f4121a86ca8b452438ab010300015c3ec1fed9e00b5a5d8996cb343852a6909f2d0ef5cd8fce2831debc27f3bcdcf4ed6ffe2501e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259673bb20af057d2ca2a62652f7a6a1b8d6a5696d62a99502f368ee42f72c0190b248ca95576cde7d56fde16844376c39f1475ba079c840e241b15e9f64598d49010300015c3ec2034caacfb3a7589034ac2d199130370b2cec5c91b5cee0352077695091603148bf4102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325967eff921cfd6d28bcc9a5d62c9939ecc3b067e759eeb4dbce056edb5a759ab96010c1162dbf50da8e17f4050da214ddacac6fe44e88e140d83ad9109067edac0e0300015c3ec207e4229ac93aac5050ad423986259c8fbebe5ac720ea4593c281b57b6745ec90cf7a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964f7b8fee5365cec2ea37a767a213676f3567b7d6d7495ffa5dbfcbb9fe49e2937656389a7a337408862a7b8a47f7a17147843c0da58d2570b64ea5b0cc3635070300015c3ec20d30108e4ceff229df1ead133f8701ab47d46b36086dfbf56c6332a870c5b02b592301e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963fad95b737af65f60796d6e335b798d0ba5131170b05c8a8dc6c8dd32bd853c8a77d2dd606a30a55e9138b60aaefcf8960a722719a42bb72dc3a939d7f0659010300015c3ec211cd3fcd69417f02fbd2bdcf5c2f5ba019577fcd53028513217f5506a043fe9784b502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325966e42a1c25ca05cd7c701910c9a9e7dd1b69538cc6900221660af7ad89ae2efa8a8eb2e546ccbaecf31bfa3294a00e4fcd7df26caccdb3ab4ca88aee5d6b7f60e0300015c3ec216933ba6d3dd3fbe6a9069183d144b2509a1e867dc3bf0417199d31e074186116e1502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596fecc0dd9bccdf384d6163f077ec871c1b0396feb7b7bbfbfa10fbdf26e2254a456dec5cf1112f909337a1f6b0ebf546a62bbc66d10f088bfd91e114f6bb8900d0300015c3ec21ac135f61dd3c0ac8008c106ac8b91e349bad5b9a2301ebe0869338ba814c47a21c301e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596bfb2e5a0bf902ec50c233363bbc67eced49cfef1b5b24d52dfa3e58742ebcb3a6786e2f33ffff90bf25689ef60cda0a9ab956d3c9c26dd688315a10e00c7540d0300015c3ec21f4b9acd6de2d7c0f5c58ea438d638f5794196b5e42da29f48bf079904f56451155201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e20ba87dc372ea0518f4b34c1f4a6e94dd0a81b6406188acb5ae106f27b680002fe994c7149ac7ecfb4523784650c597607b8dd1ec479402cad7e9f53a43dd0a0300015c3ec223ef5cab0a7a9578487102530ec17dee2e0714b9f00d632e4bdab8aa2b8f01760e5502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e85149a88dae62150a8f2a632468554dc57475c90489320bae6e82288077afc9fb4e85acf55e1491593421b14fa82f4955fd2f92e0d66d9458fb2169e086730f0300015c3ec228bf20b23789475af2b0b7195e690931ffe1c857f9ec747293f98297b5bade58a2bb01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596513a443ccdbdca91b46fe41147a972163ac9e1d7935d4a932b5b6f924144c440ebcfa9ffa16f13e761aa8a78cf660859ba7c9a5dc73a544d8eb461cd544626050300015c3ec22e72d67366fb7ca273feb33e54226a986e3cebdb0a108fda3c455419db5aaa1d682202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596c00f978b7bf73e0fc7d26a08eb36cd8a81ebec69ce5fc00160d92d3f5ab9929f7d33feb4258bf77a673adafcd4cc8639132745929f574f6703f033bad3dad5020300015c3ec23305a46dfafd124fa07544f0c477bc3862312cbd2b80885973d6521197ba6b5a5c5a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596244041b3192e46fa5ad05c69cf797311327a59d18757dd8406ab1fb3315549328a490c563e76409317abaf554e87093559522eddebe963fa2a3cc9a8e9210e020300015c3ec2387c60f99861fcf1151a738962ee2aafe7b62c69fe508550be250ef8a665912308e202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965e1e92300cc23c940a7c854492a6c8f00d1e6abab305ffe353a3bf03dfe015194752d98cdbf802d5f7b37849153ac183ffe1a07cb2b7d399a56816f64356e4020300015c3ec23d3926c587f21509ed93d5f917a2eb158981197f5183f4626c9366cf79363d36b00901e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596da8cf0ff673fd8cc379e40bac775bf66140413d6ac49bfd038f767af51e445443d088c08389cc2f185b6a08782b9e9189e767eadc4508b808dd04790cfd2a7000300015c3ec241816844d5e2752b6f4bace433af667e81fccbcc6405111ada479fbe270ad143a86602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596895c00f5bdbd0b6bb31c10a89d3dc85bdab95a68c268cae2cd5e932ae6fba5786a2a57d2fc1acd08ccdc8c282502a4658c92eb73a2340333fddfbe8f34a6620c0300015c3ec2463621cbb56040b159aabfc192a5ae1ff2dea31d02905eeca293f756249c66f2cbdc01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f47bfa5e6903fd68e0674b99a885163a123e7f8cf30b49a18b57c98f562d0f4ef12bc6eec86e6e2f0ab6f3872ca3f2bb72054c1fe9f581de23631e5d43d95f0f0300015c3ec249c7d7babe2b1e0a692c274390f777229aa5af4d355e03a3b43b493c7b1bf641246202e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963cc5358ede447da9ef0268ece7cf658399cbeacb01f5d2230aed1643426f54ad84f9097115f0a003d60fa3790088ac4639be63068b145382ced6f918408d290b01060300015c3ec24f582c281082f8ab2d4873a1d0acf47ae9c44ba0a6936c44a5897fa3113032e56a4e01d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de007fb933ef2d5d0a06a9c718c7013d56ec694479f4e74eb30fe85ab3f83d6a5da3ad06d8b2335d23160f3333868792ae37209070fd3fc944fb575074f3f24fe3080300015c3ec24e9cab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f151f47df653c6cb1e447f34ec83accb34622d9f3ce0be9e727592eea1776727390e40667d6046c0348f089aa88abfecd3e24a8f01645c852d57bc7334b50c2060300015c3ec24facab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fea2001c644d2a7ac3d7a844995fb5c4c5cac98aed73c27fa33b8649479c9ddf0ac1514d218d555439cfc007ccb60d8adfad10ed38ceca75280243ff3781bba0d0300015c3ec24fb011c1c256fbe31db95bfad7f0f2fa25c29c09e84b3d08b1efbee1e194969d4cf302c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f015cab415e6be47adefff589c54a573f4d65aa613eb1bb2e7548bbcbc84f9861eb3c1d1f29dcac4cb649a728e66e0488f2a77771707f6ac4aaba6978abda73020300015c3ec2593919788c95c1b938cd55633866f3c6f6507a9bbd291ae33537a8b443d6360c6a8902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596bd649e3831d584f7d664e19bcdf4492b83eb48fa25c3dd69b675424b1c8ac6b0c67fe2c94ab08c4b4b6e552b22c02e1b2ad1aae72f0fe5d6bbc0d64662d9de0e0300015c3ec25dd8f409a23d281cae5ff441da4ed205f25618e269e5dd7d64017a718a625d0f44a402e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596cf835bb5d5cfcbfe7226155f5f46cf74af1c1465194f99a749ace576c605434f849406eaf17927cf4040cdad3d4bf79021b1cde011225101bf076e0f2e67c4000300015c3ec2631381cbc3cfe752428023713940468b1465c67eb84e51d341cf96d0f03047902fb602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969de8a0686a17b7556a618bed74ce65947964ed8bdf1d05787d93647a5a43ca0606518f4d6aa166da6a8b5d07699663cf466ea54445b25d19267da1f6312b6c0f0300015c3ec2689c4d5ddc57d770a3c63745b0aabdf1e15863c6f09bbe866e90629a1fc34f81780901e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d3a751d034274ebb71eedc5ae19f590aee9653f4770f251e903835bc0d192a68807a0a2aab26918e2ae47cd3c5a723abaa1ee8f9c016b17e9eadb5830faa07010300015c3ec26cb672162bc224d5a0b29b31bb6280514219e48c9caafdc9c3a69de34b6e02b3f9c802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d125216b5d5f771a9c8cc9b1b609f54d4319695c75fcff9fcedea5b84bb57307fc9ce7a56f5d24e434f64822ac0f4615f12c5c5ec6387c0166e03597f5207b030300015c3ec2710dca583626f598971b1d81a5c6f293489cdba0f2749110678c808206badeb5c47702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259638435df852f32d6f18303ee1cccd991acd000276deffd6241ca457a4acfba711bd328be8c753345ecec6911d9be437bc9d5201dbad0d1e66b84c720755e693070300015c3ec275fb009a06921d67083ca79fe165efaf39a8c5ea6a6d680863ebb0958f9341d3010201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325968b709e562ff724f0f53305a0da3167cb4e24da14c581ea1a3fd36104c8d1e5cce82d6238eb800553ce95c26e27bcaafbc230663e7bf1fa3a01c69f8b1cbfff080300015c3ec279a320dd12384286056f658805714b636fd775e01998c0664a1143ff29fecf26a0df01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964e72355152601a57c7c9a6d629877ca9ca40eb257bf0719aa85a1dde70703540b4388b3402be0a237f02dd07337008160b4de33ca917783cc9d812033e44af0b0300015c3ec27e17bca44fdec5b3a00f50ae6ead3f7e6b6076fe3c78a90c2cfa83d518a3b0bef89d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596802995c7013a42c75aede486d348c2a14a50c266426376167e428695f67dd2531a5dbb2f115057a0dc23936b54ba6bc190431f0b5c506dd72297ade3e68b360a0300015c3ec2823113de63ff32951d90a53631508d088c941b54314dbd4733540494726c945f8f6e01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596f45aa007c6e9630612402c4bb342dd7f2c586223e25a13648c63127d125e59aa9af5962b40ff8cd541981cbc8d6c841e0bd971997e4284bc7bef5c73c07555000300015c3ec286b795668d7fb90de03d3431aa28a74d009542311a98ee15dd886652d9f639f128df02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596c3aaf2174fb3f98247daca07b57c29b38ca7c5ffb877dc5a675d723e408e187f437e6ad06a590882f5039a40bc2e3c20b22d29ed96b13e8905c4dd069ee5390d0300015c3ec28b14796f10d24992b031df5d41994d8fd9aec0b791705dc25d384fb1e430fc061f9101e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962591ff0c640adbd7f187e819c80c710b96eae358843e9dd58dc86f80810603e0c6b329377a570b9a3c92e0ba4390c097ad25ab16cb13537b55fa58cbaff0f9040300015c3ec2900b7ce908761d92ac7e2900363fa8e83de9fa8596c800ae3a09727fe48ad0d62d9b02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961c1dd5cdaff892861f23e5819600f8319c2820ca84180de4fb2f48fedf72f77b3a9c1e9c895bff3f6b1b3dc4630b98559ae4c5875862bf2a03cf16ea2ae6660e0300015c3ec293f35124439e0f4f186a85c0008cd68aac61419fcb1d2681c0c75a6e6661ec2fc8e701e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596fc097b44daa50f1f2661b66a11d7cff1e4507c0f63cf8047ad625d505e3eda258419f08f3a3091194489d9e8ff8790410362ca9dbdb8a2ae22bfb58f669f91080300015c3ec297e68348b48015b182f713e38f63be5f509524477e6e2dfadb7eb76b0f905bba99ed02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596849a24af320ad7d4250f8172b9af9dc651492e422ac55e8c9ca359f54a87270e2530d5d7760d127402ad87d73f489f1c3ca239118103a8aba14cf8fabf60a8030300015c3ec29d1183f1e8089099b82b16430957951a671d603357ee839faa0d24a076e42caf0e5902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325961a10b484296ad255ce9897e44fe84feec12c583832e6fa7bc3e92d1ed4daf69f48f1f9e58c1fbb13ae8658c469930dc9a3a75f7b4735d896773d0073a377d3000300015c3ec2a18694f0003f447376bf854d991768312da62426f609d16475f8eaf03ba9eadf02d001e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259683765646f8bcb49f3cac4ae62f151a9c0111829c26baad42b32d9f1efb127b84c3ef10462a50bc72fba7c6db89c513a2992a7b38741ac1c04827d11f4c153a0a0300015c3ec2a647f2668cdce36d25556e9ea90666c8a79bf6be486adff7be67383a711a1fd7cb4702e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596099364ab046fea865e3f701495f66a2a60246e1645b96966ec3decb5596479bb3dd54a2a0e30e7e301cb35a86f752f07ac2fb9826af7d546edb11cd17e8b67030300015c3ec2abb1491e645f0f719f71aa440a63662bc2eba4a6978ef04e6547a8d93e3846d6785502e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596134e6080ddd04c92b30f3076bca838c2d917758c1578e31fa811dafebd75593c4e361aae8ed9ffeabc01d34a304d448a92a1c7a638dc36bd1922a0f0162cfd060300015c3ec2b06adf6e2a612c9a2136db76679e722121d995c2aab20c931bb0c8f057e53977ab8002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965a15bbe82566982880ff59f508e5dfeb46b1a1318c68168f96718fdcff5cbb46d098e9cba0ec80e537bbfb48c4fd079d7c13ac4ee4bce2bbe983284f86db160c0300015c3ec2b609f872538c8852a8c13bfbeebcb958928ee8e2050296db6f34279eba19fd53d2fd02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325965abf4128f9c1cc5235a5e90010134b7bf1b2125750f0d08228d2acac28548eb442a9bc97ca16c55f8835d55ffe2eb524f00e1267653d371adf119a8b7a7082040300015c3ec2ba8ad1e6c180bd6b0bd1a334b1806f0bc4f13ef36e2c2e3a9fba504a23604000ecce02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259640dd9d7e6e39c0f1e1623f65164bdcd50b60896203e68bbe815e8104f12f52a57893a4fbb4de81097c0be1a9adf131739a22e251efbd49e22b9aa9c31806dc010300015c3ec2c041117154c4f93cf05532e823508c70ee2276e7a8ae640250c97872fa74b53d2a8601e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596dacc75b464dd44cc133d2942a4b5c057325f88a304578a86e5b464538460e918b3a5f1976e20ef46c45b5caf49c25bf14abd3204058f92b98046d4519a9ce1060300015c3ec2c4ea97aea60775ae75b0b555dedb5982c3f508836d4c039d93f409038f30e39f8b7d01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596090d87adce20dadc7901b2653e8fe96f42ee350b012c2475917e38532ab7fbb5713f034abcd029a3955a99340ff4628714f616e0c8b22fa1d66e0fef7c74ae060300015c3ec2c919143f66160a7191a556bfb51c27c5a17eb8d9b086ebda34895b144639d5f1a01b01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963cc37f2217467c602373261e1ec13f454852df01d45df399647c25c5b4d23e9e7bbfedbb01148b8e84d1a155216d1dca18e1fe86e002f09334bee193da642a020300015c3ec2ce2358ab02ab631ec812a8c3b6a355cad5e603f8e7dc4f292e88b61c7c147e61635c02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259682dc7c08682942d8e753cf3fb97a18dbb0195fecfacc6921b7e2f5a5b4063454087de0d96ae027037fd486ae463aeb477fd157b927b218d53c4bdf883cfc8d050300015c3ec2d2c7836d08b547e38c6146bca2a0a1f6cceb2b72560892f9fb47e4e07f288764124a02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b2297e0b9c6a1611d2e8fc7cc1cfa20e66018fa9adaf093445fb961ac2dcb76601116275d72a11e386c8658d3756162951847ff79775400cc270851fe2dce2020300015c3ec2d80c878a1fcd948123c554351a44fe857c030a3be48ea079b58fef379b0e70eef94802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325963c170b250fe0cf412c7f72776ed2b889e0bfa66650c72d6d5074fa272d4b90776718d0afdcab3c8909976afc47ee55c3fdf02d54a949052d1b39db2d774e18030300015c3ec2dd12c138376efa5472363e3ae9ad141aa29d72ce1d9f138d0da87d96316adb1c896b02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596a806d198226756df5d472abc4ebf7fc0684cdb1ffdd5c3fa50ca837263679be9b71c24f6e8de1a71ae52f61bc05773accd115d8da90069122027102325d4c50c0300015c3ec2e3297eed43e38dbda2f69caac55c39a739d22c5e90f3856eb44f08c8084eff7c075902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259610e233e18fbc47a9301e90d3299d294eac4301240272cab3fac30712e66932e22e611d9f38892653b14596b550d915509a079d5636c64bbe7ff6750204c8ef0b0300015c3ec2e9c1f7403b9b15ec3381f94a5b4ffc436d4f37eeacc1d31a5504afdd34ac33ef46dc01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325960e99bd087727c19873abadc93ece069731edf3d2bffe709853140ce17eca7d18a4bf37caa71b7fdefdc8d69544bbd37d3cc86146a5cebb02563e4806f6ad080c0300015c3ec2ee7ce597a3706df5b762cd05204544689c2f0e9b7fa96f261a804bb66c249b3a7c6f02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969726e3b15b808d16a523a818f65a8d815357b8f6b9df5f61132e7a5246127946fbd196292ed670433db93996f6210d6c3298d2a5cd006cd8dcbce3a0d721c60f0300015c3ec2f41c292113a36cfa4f7e8265cea80d6307e059fe4b68fd2218e434af60af33307d8002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596207b6a5775cc9d28299752d750e023793858cbe073af9038717e25bda052ba37855a92a1b768406090d63363a0ca9efd4a309215443eaf960a97db033a06e2050300015c3ec2fae7ae7450af7e6e646fe7d0b5b6bd149ccbff66fdca8bcd8d519bc7fe58854e11a601e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596bb1627ba87b2169517a2a49e137eb7ffe470e9db454c3ceefc0634320b5b4c55cb07f07216b73d7481498a66e362be0a86afcf6c5cf1ca5ba0540cf9d8642d0c0300015c3ec300335bc8311e7a87226e086d36b6a4411ada0f97cbc57eb1d9a988fa90f52b7529b201e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259675fee837f3c5867b211b8ec899a0b0f6ae6a77560da6b1aac89967c04598d530b199be0e9f78de7e00bfb51fccf5d5bb3d20f28b95131c2a44b284fbfd8e35030300015c3ec3061acd10c58d077919fb8235256eb24218f354850e2f22fec4d8314e5979ea54544c02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e03d1cbdd8840b68da6df323e0fe05d7c0d95ad636805cf7ef09bec6e7f43ba0c1c8a2c50bc87c8fae662ffb3eb6955202d601ced72277d3a1cca08b03045a0e0300015c3ec30b0d759b331c5efee501b4c9b1bbd3563e0877f684028cb0d1e353a237003dd1970802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259640862252a6e72e2bbc1db9294138428764efe262c774e4ce7703da4ff54b3cc9891db481d97183684561f7e11062aaee8616bdf2a25e9c7b895a25ef611ab20a0300015c3ec30fd70d1cde89667f3946d0a09fd77bf9e1a572bee5e110110c7773fafa3c6157853602e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325969f88db760d116685c3bf0d959f0816548f97dfb34a94055bba498e1fdbf5c0580ff3f041fef96e37f333e96f1303afe8928170af14c2b4363f9cae7ffc6a6e0e0300015c3ec314f20838e347efd8b5a44bb1fc5b67c56d6fdf748c074d54bfe69939efd937ec7f1902e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964a4f3cb456fc6a0e25476fd3972fd218bc62cc60f7e3490b1931382c4464d7ac9ba336eacb56f174d218b2df2b90f0879fbe127764a6907febc6f57700ff7c030300015c3ec31a35e0ec1a3fb51e502229782d132351c63700a341e77c2f28aab80afdcc2cec916501e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596af8990bbebe9b29977d26032404e2c809d76f5995b2211de457503ab467057df8322e7efa4114faca8bdebb7fb75d6d089e841b6db15a708f9be777f5e7db00d0300015c3ec31f8aad686ce734e232bbf2f35009672f6b09b17bcd143a373774579c67eeba91ed5e01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596aedc84db5216554b09f0cfed18972c7e8fc5aec067cc19a2c524c808c057dd6f964df9d25a68fdc2e369b6a845f8e43bc9c2c6591c37cb5957648a0a77be580f0300015c3ec325032ddebfc805045719752c5a24578a105506343a98eac45e7ab4aaff19ca73ea1802e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259600e5718a5ae578c081bcf9437968d9a761c17d62682377437f982e6151d448f454c9efa739d1aca93ef27727ca1357c425c04b8d0263839b9d64a499b0dfae0d01070300015c3ec33b67ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f4541cd1bb61dd44a55e6a0c1c50350dcf5341639c1a3e3490c11b494e679c26d0d0df366d6a31f6932b094ce68ddd99a6d8a31a578b73f8ed872d78caf58aa0b0300015c3ec33d0998885648bad57207ced1da127a73bd5c816e8130fd3e242feebf7b22a351fa4c02c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f0bc6156a0233286423f36a3ed355d396a7ebfc3c875c41adf6756931a1e1e8d20a208040cfce2bfb596f7f8ed3318f499957cc855ff4a28d5d5ba83e79c7c40e0300015c3ec33d14ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f46909374b46bf915f3c74f4da1ce67e686efdf494ddd78efe0e58401d2f95149cec5dff7be44095aa133e276dbc7013869d8f30acaf96a2f8eef84a399a8db070300015c3ec33e88dfbb11f1a3b32fbbc5c880b88353269e5c5fac6295755e25b0edbd3781f57c8301154d0d8147132e8c73f43f3fe9159c9371743205ee3e658dc269e07992db0610d8f0885743e4aa5a1def1e5c62925262b74c2b4382825a70d3244ec4f5a8c5b4c8d149683f00b2d4b432e45d8751f3eb25a16df42ae422498dc4ffa23626f7020300015c3ec33e424a7062bbb4a85199bdedd40d6cd8154d6f68d726d7962b6c188d7f1975e172f4014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b791ac960d95051d79bad840160e017fffdb6f19cfd5534dadc9bd6688724d34dafbb7592383e4d8a1e9dbd073ce2d11d0ea44d78a5eaba6991dfd03f71c8ed20d0300015c3ec33e60191d7771761822709e869e6ec1c6d33cdc5c9969f2637731cd6c81942649f1dd014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7209672d050e01f7adbafeab01d42849b603ff4397b8f4f2d121a81162c21e1c610bec3a7fd3a2c864ece16f7f8f87d438bc65b8c5f3b8705a3c35ac64036090d0300015c3ec33e383539f212a9f11743e051f280992c643894c7c09fd42836c6a20bbe66d75a7966014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7a7a9efea68b4aa5a5ebdb113d96b55c8d0140ddab98f2a81bdf822825db6344051927b732d8c9a14d757e3cec3a368b25a686a93cd4afa5fa6a12c389b48aa0f0300015c3ec33e3d9ac1db64d3dc7948fa11655a259859751c335b049baf185de4afe06875b7892b014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7c5a4215d079a13091644e5610a363bce49f82764580eb835254d472b70539a5139ae564e4b88cb4b645fa30363e5694dd519bb2c00352bbbd7be543ba655fb000300015c3ec33e48128e544c5bb6a303801dd5c477ec62026af8726d155e39bd365402cbcfd426ae014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7ebcaafbcab27848af014711b2338b8e009890fcbc122c6e7e7e757e9b223071d32146879dd6b7cf09e9d4a5bff2f9664326a0964ddec7b2c6ae3f845d6e229060300015c3ec33e417cb0b9c31baf965c90b5d9747c55d40086422c4915d7f10cea73622d4ba752b4014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b79e11ec3321414c786515a55dd761e12196d8f183b9e5010d7d545e1ce689d6f13363ab0b489aa82e628c5cb95576f86c654d6cc88bf77664f9105dea0dc3ea030300015c3ec33e8d3180275c493d1f28079b9dcfd4f9d28b7467f3bd071371540f8736125aa4c9db014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b738b80aed6a3d26ad694d054cc2312f13e3e39371f122360c20d2ff991aedb46468e51b61cbe86bfa23f21935d32b0e42e04462ded513183c969d1da1c5b711050300015c3ec33e61370054e235209bda68f934c8d4bd9d84edef4c03ebd890fe7686edca138f6188014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b750318ffea7ba7b21492724312de1b7ebeaf7a37c1bb9b5cd8b7b2c2067587a6a68e40352d5b14440778f45db91953f5d316c8b5df1e651f60fec2d948940f0050300015c3ec33e8e8cb84504591bcbf076a711e12cf2a7a506fc83c28b2f8cb7de4b30eb07d19c14014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7e472849cdb365b9114adcba8e7188b1c14cd91abd196dc1230b8d3c0ebdcd2770abb590f8dcf86c94f8df7d38a328722683d78008f0f0ba99666d9665e3a04050300015c3ec33f1da6cd017b32c21b3d1af53e925eceffd88c0bb6805834d2b912a24aed211e5abe011c078c0bde98d9b26032e1e2444f0627f2a9dbef1acb2f840206cdc6d82645f8c893058f6e0e18084e9e61b70dd41c86426e0e7d7851d6be447825a03c4404ea561327c0be659b894a19d3c8691f0a88fd15e763d1810fa3868255bfeab4500c0300015c3ec33e89ca900f6d10d7aab8d511448baa9d26918ecaec76ba634a78e1d05e96132ccbd6014bcbc1c5ab90e432bd407a51eaa513b4050eecda1fd42bbf6b7050a1d96f94b7521b5aeaa44d4c341061d5530341df429af12610dff2b4cf2a264e9f921a9f82ebb0b5cc91cdea559b9fb099e0d309c1b04588e03fe503036520a7ee9541e8050300015c3ec35ed2ac460ac17862493f062e48a3332210554f2fda92d631b900efb23fc8b874cead02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596cb1d37c02022259c5443df1ffe6dc4c97e508f6844d8db5ef1066e05a9918bebcbecfb21551541002607cd9d0afbeada179d2bb9a1fc7270a84c7df63362ea0c0300015c3ec3641eb85bb04900bf1993ef61557cacde3818c3f4ce4c139600c686aa4157ff241bfa02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259669982dff7b6552c423933eb2cb04b216cffbc6ec519d8a95caf4f3cbe210909d7981f43da1ac3491e0b93855418ad214a5fa982392aa662b9cfe36eb180ac80b0300015c3ec3d044a6e64addbedec2203e8e1249d818ff526fd4bcb9e6f4301198976bec55ed3ba002e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325964952950430c07791b37d40b4fc28c748f52dec67184c7bdc9ac642a467c2ff26ec29f1c7ff8bdca655d7b2b482d836a53f13346409fb1aca8ff2f35fb225f2090300015c3ec3d7a280e9b531baf6c559ac5feaae55f630046680e16777c33b9558d666e92aca6d4d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259631e2e85aeaa33c900b38ab713419cd2a1bcd66a7c13d090ae7b4afc3f6a5cd54efd915b4275a72e5aecf21559c13cbffa2c3f2f35d024dd54f81ec5adecebe050300015c3ec3dbf0941704dbc6a753ef576aed92ffc42df97c735ca710c0e5e0d7a757853474686d02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c8316325962fb1c5cef9f235d93402c8bccdb151572e5db55ea368074c73e2c837c1686fa0608e2819712772a4befef5f8ef98dff90c7ca330a917a4c3c221f50fcc1435090300015c3ec4121725c6973cd69151a31f861c6c7ee486721d49856e6a9fcaf32f2c46237b78ad6102e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596b1795a76fe0fab4e22285830714872ecf12c8e3e4e6709ef1f67616fef3babdc8d1e2d343c146caf86d1eda5627350293b69cdc4679df2e0dfe42f6715f6b90a0300015c3ec4169af9d7ec4039d2b214b999f5609747acf6da6a001d9de4c2f3dc57bcbda19063ef01e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596d7974578e3473681b34e23a6cd1080b3ff8b9ecdc3ed3df699f9850bead9f2a92c27e498a43334b3a27334f3f5cd2ef6083c76d1e49ff9e3e5f70a38d67f9c060300015c3ec41b37ffa4c452c1f6f63e76cafb430a9f55f974a75e92b540e9952e5ddee411c35ae401e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c83163259666a0120ac760c504a73bd17e7e6fc371f4641e9740be83d0c5d84aab06385c48b8e305a1e6e6a6998c9fb9601c6e4497995bfe624ead31e378ea54f17e52e7020300015c3ec41fffdc5fe37288da2698da96ad396f5b35e4b3306d94446be2ba663c46329a2e6a3001e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596247271ef312b0d91abe9599f853ae3c90b9bb527ea2372bedc029a0d209efb414b94379e7bdafb9fe39eed9a9e61342b73ec18afd3677ecf8fdde909d3735d0201080300015c3ec42507ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077ffe14d1b2b3713a35ca6023fc71ebff6a7b2a3ec6a91763122363dc1733c782251413de53d071c6015d0cfb64b038d179b2077617a6475fb08cbc1dfba7381c0f0300015c3ec4265059ce52d00c6756bc297593123b952bd24d3122d39e1be16060f5672619cb0d9902c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fff6d881387567ced403adc277104e86da2720fff57c6924a045ec5222974e86dc6820adbd202076575c46d87eaf046ea2c9287e34aedccc1979de3854a6d390b0300015c3ec4265fab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077fce8259a9a7137b75f722deaeefcbbd682080d8706ee2f500cca530c6aea82dde3911701e854db155eb0c86a3ea5aac6fad339cc53f20d224a55e08c3e73a0f000300015c3ec42546bb299fe3f9fcfe59f188106d16772b8f6c9befcccf03b4f2b733d7def4dbb9a501d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de00ff64c23d45a8a5ad618e386fd19575f3dc12fe3338c7c8b4b062e52cdd59785f5513e74054472550eb03a9f29c889b4937b23beb0c26a75476355434fd536d010300015c3ec459a8dc7561838297fd171b4be24c0442e739dc15338d2acd19748f5ab253d1ed83df02e52e7edafa6fddd8fcacb11808fc0d9d6a7cae21bfd359f5f3aa36c831632596e191c7728d64f87e39882ecffb5adbf58f023daa6f243a5cb9346f6dc65b488520b33e6cb45d33baa9bccd542a5a0208f2c85eec9cb857e309b53cf9ed1f8b0601090300015c3ec50f1d2160d40259e4eb762aeb32956319fb8000888aa64fc366f479e1d35b6714402c01d5b8efdb52a15c4007d341eb1193903a021ed7aaa9a3cf4234c32ef8a213de00b81797c95ce9cbc16106fd659c34ded07490dfadb4be7e885b274d3dcd896b88c80fb0c9dd7d8718ffa1f1041fef3454b536702fadbbd347d51c9c0f9f22900b0300015c3ec50f18ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f73f87372d1eb765db7a7f161b5d0f5a8e3a5266ca18cca251bc2dd6f31bd5bf3ec68a12240daeab331a9fbc11945a8f317da1e46ccf1666a7c306cbf0f6f93060300015c3ec51042ab5db991953716e855a43bb715719370a9c3e50e3443f09493a900952ebb033701c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f56712370faa78fb82e76ff4bb2e21d09fe7ba4e2b20922f86d831efba3e0448beeef1f832d5942cd81c072d9052b9361460f30065cd5f91108f055f41be9350b0300015c3ec51058848a9c2bbb147f696f957a025f9904a9efc40c68a8386bd35e63a29f5e08958402c4c8e49f1f49167dc1bb143c698521f9f471fb4248863bc68f8df0aee697077f6e41373acf313ecb331137b44e71f9b497bb4d927d524079f0069c298d442be1369b4c26f79692269c67022a79015bb1fae8efab62442f9c8e0964cb53c5290c0300015c3ec514402cc72a88d1d9b93527abc2f130a715cec48a44a5da48f2110050fb39edb4d04501993e57f755ee402ff5df248841f7a621f6d2c08f0616c5ca5fed983d102db610f09712b6a5a5237a49db31718e55ff4a0433b3cb617cedbe542bf0ccb8550eefd78fbc32f0164a7bc888800ac0e288d5ae57a787c8b42917599fddfe8ad6820e010a" + t2.KeyMR = "ad7b26cbdbc40c2dc5b966c2555570f3a03161a0e58d88fbcf9d07fee727ee32" + t2.Hash = "ed6f0ced32900cb1832d221fee28102653e6e3d7eb33ed7a32650cb86bf68806" + ts = append(ts, t2) + + for _, tBlock := range ts { + ecb := NewECBlock() + data, _ := hex.DecodeString(tBlock.Raw) + rest, err := ecb.UnmarshalBinaryData(data) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Error("Returned extra data") + } + h, err := ecb.HeaderHash() + if err != nil { + t.Errorf("%v", err) + } + expected := tBlock.KeyMR + if h.String() != expected { + t.Errorf("Wrong hash - %v vs %v", h.String(), expected) + } - h, err = ecb.GetFullHash() - if err != nil { - t.Errorf("%v", err) - } - expected = "1eb3121d81cd8676f20c5fec2f4e0d7a892a2ab2f086506bf55735756098d9ba" - if h.String() != expected { - t.Errorf("Wrong hash - %v vs %v", h.String(), expected) - } + h, err = ecb.GetFullHash() + if err != nil { + t.Errorf("%v", err) + } + expected = tBlock.Hash + if h.String() != expected { + t.Errorf("Wrong hash - %v vs %v", h.String(), expected) + } + b, err := ecb.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } + if primitives.AreBytesEqual(b, data) == false { + t.Errorf("Blocks are not identical - %x vs %x", data, b) + } + } } func TestECBlockMarshal(t *testing.T) { diff --git a/common/entryCreditBlock/increasebalance.go b/common/entryCreditBlock/increasebalance.go index f2fde3c3a6..4ed85e836b 100644 --- a/common/entryCreditBlock/increasebalance.go +++ b/common/entryCreditBlock/increasebalance.go @@ -117,52 +117,50 @@ func (b *IncreaseBalance) Interpret() string { func (b *IncreaseBalance) MarshalBinary() ([]byte, error) { b.Init() - buf := new(primitives.Buffer) + buf := primitives.NewBuffer(nil) - buf.Write(b.ECPubKey[:]) - - buf.Write(b.TXID.Bytes()) - - primitives.EncodeVarInt(buf, b.Index) - - primitives.EncodeVarInt(buf, b.NumEC) + err := buf.PushBinaryMarshallable(b.ECPubKey) + if err != nil { + return nil, err + } + err = buf.PushBinaryMarshallable(b.TXID) + if err != nil { + return nil, err + } + err = buf.PushVarInt(b.Index) + if err != nil { + return nil, err + } + err = buf.PushVarInt(b.NumEC) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (b *IncreaseBalance) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling IncreaseBalance: %v", r) - } - }() - +func (b *IncreaseBalance) UnmarshalBinaryData(data []byte) ([]byte, error) { + b.Init() buf := primitives.NewBuffer(data) - hash := make([]byte, 32) - _, err = buf.Read(hash) + err := buf.PopBinaryMarshallable(b.ECPubKey) if err != nil { - return + return nil, err } - b.ECPubKey = new(primitives.ByteSlice32) - copy(b.ECPubKey[:], hash) - - _, err = buf.Read(hash) + err = buf.PopBinaryMarshallable(b.TXID) if err != nil { - return + return nil, err } - if b.TXID == nil { - b.TXID = primitives.NewZeroHash() + b.Index, err = buf.PopVarInt() + if err != nil { + return nil, err + } + b.NumEC, err = buf.PopVarInt() + if err != nil { + return nil, err } - b.TXID.SetBytes(hash) - - tmp := make([]byte, 0) - b.Index, tmp = primitives.DecodeVarInt(buf.DeepCopyBytes()) - - b.NumEC, tmp = primitives.DecodeVarInt(tmp) - newData = tmp - return + return buf.DeepCopyBytes(), nil } func (b *IncreaseBalance) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryCreditBlock/minutenumber.go b/common/entryCreditBlock/minutenumber.go index 4a89a89325..d5801cac83 100644 --- a/common/entryCreditBlock/minutenumber.go +++ b/common/entryCreditBlock/minutenumber.go @@ -85,27 +85,22 @@ func (m *MinuteNumber) ECID() byte { } func (m *MinuteNumber) MarshalBinary() ([]byte, error) { - buf := new(primitives.Buffer) - buf.WriteByte(m.Number) + buf := primitives.NewBuffer(nil) + err := buf.PushByte(m.Number) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (m *MinuteNumber) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling MinuteNumber: %v", r) - } - }() - +func (m *MinuteNumber) UnmarshalBinaryData(data []byte) ([]byte, error) { buf := primitives.NewBuffer(data) - var c byte - if c, err = buf.ReadByte(); err != nil { - return - } else { - m.Number = c + var err error + m.Number, err = buf.PopByte() + if err != nil { + return nil, err } - newData = buf.DeepCopyBytes() - return + return buf.DeepCopyBytes(), nil } func (m *MinuteNumber) UnmarshalBinary(data []byte) (err error) { diff --git a/common/entryCreditBlock/serverindexnumber.go b/common/entryCreditBlock/serverindexnumber.go index 00a9e0c641..96d898d2cc 100644 --- a/common/entryCreditBlock/serverindexnumber.go +++ b/common/entryCreditBlock/serverindexnumber.go @@ -96,27 +96,22 @@ func (s *ServerIndexNumber) ECID() byte { } func (s *ServerIndexNumber) MarshalBinary() ([]byte, error) { - buf := new(primitives.Buffer) - buf.WriteByte(s.ServerIndexNumber) + buf := primitives.NewBuffer(nil) + err := buf.PushByte(s.ServerIndexNumber) + if err != nil { + return nil, err + } return buf.DeepCopyBytes(), nil } -func (s *ServerIndexNumber) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling ServerIndexNumber: %v", r) - } - }() - +func (s *ServerIndexNumber) UnmarshalBinaryData(data []byte) ([]byte, error) { buf := primitives.NewBuffer(data) - var c byte - if c, err = buf.ReadByte(); err != nil { - return - } else { - s.ServerIndexNumber = c + var err error + s.ServerIndexNumber, err = buf.PopByte() + if err != nil { + return nil, err } - newData = buf.DeepCopyBytes() - return + return buf.DeepCopyBytes(), nil } func (s *ServerIndexNumber) UnmarshalBinary(data []byte) (err error) { diff --git a/common/factoid/fblock.go b/common/factoid/fblock.go index 41edaedaac..56f9b8d8ec 100644 --- a/common/factoid/fblock.go +++ b/common/factoid/fblock.go @@ -5,7 +5,6 @@ package factoid import ( - "bytes" "encoding/binary" "encoding/hex" "encoding/json" @@ -44,6 +43,18 @@ var _ interfaces.Printable = (*FBlock)(nil) var _ interfaces.BinaryMarshallableAndCopyable = (*FBlock)(nil) var _ interfaces.DatabaseBlockWithEntries = (*FBlock)(nil) +func (a *FBlock) Init() { + if a.BodyMR == nil { + a.BodyMR = primitives.NewZeroHash() + } + if a.PrevKeyMR == nil { + a.PrevKeyMR = primitives.NewZeroHash() + } + if a.PrevLedgerKeyMR == nil { + a.PrevLedgerKeyMR = primitives.NewZeroHash() + } +} + func (a *FBlock) IsSameAs(b interfaces.IFBlock) bool { return true } @@ -219,6 +230,7 @@ func (b *FBlock) MarshalHeader() ([]byte, error) { // Write out the block func (b *FBlock) MarshalBinary() ([]byte, error) { + b.Init() var out primitives.Buffer data, err := b.MarshalHeader() @@ -249,76 +261,106 @@ func UnmarshalFBlock(data []byte) (interfaces.IFBlock, error) { // UnmarshalBinary assumes that the Binary is all good. We do error // out if there isn't enough data, or the transaction is too large. -func (b *FBlock) UnmarshalBinaryData(data []byte) (newdata []byte, err error) { - // To catch memory errors, we capture the panic and turn it into - // a reported error. - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling transaction: %v", r) - } - }() - - if bytes.Compare(data[:constants.ADDRESS_LENGTH], constants.FACTOID_CHAINID[:]) != 0 { - panic(fmt.Sprintf("error in: %x %x", data[:35], constants.FACTOID_CHAINID[:])) +func (b *FBlock) UnmarshalBinaryData(data []byte) ([]byte, error) { + b.Init() + buf := primitives.NewBuffer(data) + h := primitives.NewZeroHash() + err := buf.PopBinaryMarshallable(h) + if err != nil { + return nil, err + } + if h.String() != "000000000000000000000000000000000000000000000000000000000000000f" { return nil, fmt.Errorf("Block does not begin with the Factoid ChainID") } - newdata = data[32:] - b.BodyMR = new(primitives.Hash) - newdata, err = b.BodyMR.UnmarshalBinaryData(newdata) + err = buf.PopBinaryMarshallable(b.BodyMR) if err != nil { return nil, err } - - b.PrevKeyMR = new(primitives.Hash) - newdata, err = b.PrevKeyMR.UnmarshalBinaryData(newdata) + err = buf.PopBinaryMarshallable(b.PrevKeyMR) if err != nil { return nil, err } - - b.PrevLedgerKeyMR = new(primitives.Hash) - newdata, err = b.PrevLedgerKeyMR.UnmarshalBinaryData(newdata) + err = buf.PopBinaryMarshallable(b.PrevLedgerKeyMR) if err != nil { return nil, err } - b.ExchRate, newdata = binary.BigEndian.Uint64(newdata[0:8]), newdata[8:] - b.DBHeight, newdata = binary.BigEndian.Uint32(newdata[0:4]), newdata[4:] - - skip, newdata := primitives.DecodeVarInt(newdata) // Skip the Expansion Header, if any, since - newdata = newdata[skip:] // we don't know what to do with it. + b.ExchRate, err = buf.PopUInt64() + if err != nil { + return nil, err + } + b.DBHeight, err = buf.PopUInt32() + if err != nil { + return nil, err + } - cnt, newdata := binary.BigEndian.Uint32(newdata[0:4]), newdata[4:] + // Skip the Expansion Header, if any, since + // we don't know what to do with it. + skip, err := buf.PopVarInt() + if err != nil { + return nil, err + } + _, err = buf.PopLen(int(skip)) + if err != nil { + return nil, err + } - newdata = newdata[4:] // Just skip the size... We don't really need it. + cnt, err := buf.PopUInt32() + if err != nil { + return nil, err + } + // Just skip the size... We don't really need it. + _, err = buf.PopUInt32() + if err != nil { + return nil, err + } - b.Transactions = make([]interfaces.ITransaction, cnt, cnt) + b.Transactions = make([]interfaces.ITransaction, int(cnt), int(cnt)) for i, _ := range b.endOfPeriod { b.endOfPeriod[i] = 0 } var periodMark = 0 for i := uint32(0); i < cnt; i++ { - for newdata[0] == constants.MARKER { + by, err := buf.PeekByte() + if err != nil { + return nil, err + } + for by == constants.MARKER { + _, err = buf.PopByte() + if err != nil { + return nil, err + } b.endOfPeriod[periodMark] = int(i) - newdata = newdata[1:] periodMark++ + + by, err = buf.PeekByte() + if err != nil { + return nil, err + } } trans := new(Transaction) - newdata, err = trans.UnmarshalBinaryData(newdata) + err = buf.PopBinaryMarshallable(trans) + if err != nil { + return nil, err + } if err != nil { return nil, fmt.Errorf("Failed to unmarshal a transaction in block.\n" + err.Error()) } b.Transactions[i] = trans } for periodMark < len(b.endOfPeriod) { - newdata = newdata[1:] + _, err = buf.PopByte() + if err != nil { + return nil, err + } b.endOfPeriod[periodMark] = int(cnt) periodMark++ } - return newdata, nil + return buf.DeepCopyBytes(), nil } func (b *FBlock) UnmarshalBinary(data []byte) (err error) { diff --git a/common/factoid/fblock_test.go b/common/factoid/fblock_test.go index 1912838a79..bbb48f90c6 100644 --- a/common/factoid/fblock_test.go +++ b/common/factoid/fblock_test.go @@ -29,43 +29,65 @@ func TestUnmarshalNilFBlock(t *testing.T) { } } +type TestFBlock struct { + Raw string + KeyMR string + Hash string +} + func TestMarshalUnmarshal(t *testing.T) { - rawStr := "000000000000000000000000000000000000000000000000000000000000000f16a82932aa64e6ad45b2749f2abb871fcf3353ab9d4e163c9bd90e5bbd745b59a164ccbb77a21904edc4f2bb753aa60635fb2b60279c06ae01aa211f375417362fb170f73c3961d4218ff806dd75e6e348ca1798a5fc7a99d443fbe2ff939d9900000000000a2be8000000010000000002000000c702014f8a7fcd1b00000002014f8a851657010001e397a1607d4f56c528ab09da5bbf7b37b0b453f43db303730e28e9ebe02657dff431d4f7dfaf840017ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7d01a5be79b6ada79c0af4d6b7f91234ff321f3b647ed01e02ccbbc0fe9dcc63293482f22455b9756ee4b4db411a5d00e31b689c1bd1abe1d1e887cf4c52e67fc51fe4d9594c24643a91009c6ea91701b5b6df240248c2f39453162b61d71b98270100000000000000000000" - raw, err := hex.DecodeString(rawStr) - if err != nil { - t.Errorf("%v", err) - } + ts := []TestFBlock{} + + t1 := TestFBlock{} + t1.Raw = "000000000000000000000000000000000000000000000000000000000000000f16a82932aa64e6ad45b2749f2abb871fcf3353ab9d4e163c9bd90e5bbd745b59a164ccbb77a21904edc4f2bb753aa60635fb2b60279c06ae01aa211f375417362fb170f73c3961d4218ff806dd75e6e348ca1798a5fc7a99d443fbe2ff939d9900000000000a2be8000000010000000002000000c702014f8a7fcd1b00000002014f8a851657010001e397a1607d4f56c528ab09da5bbf7b37b0b453f43db303730e28e9ebe02657dff431d4f7dfaf840017ef7a21d1a616d65e6b73f3c6a7ad5c49340a6c2592872020ec60767ff00d7d01a5be79b6ada79c0af4d6b7f91234ff321f3b647ed01e02ccbbc0fe9dcc63293482f22455b9756ee4b4db411a5d00e31b689c1bd1abe1d1e887cf4c52e67fc51fe4d9594c24643a91009c6ea91701b5b6df240248c2f39453162b61d71b98270100000000000000000000" + t1.KeyMR = "aa100f203f159e4369081bb366f6816b302387ec19a4f8b9c98495d97fbe3527" + t1.Hash = "5810ed83155dfb7b6039323b8a5572cd03166a37d1c3e86d4538c99907a81757" + ts = append(ts, t1) + + t2 := TestFBlock{} + t2.Raw = "000000000000000000000000000000000000000000000000000000000000000f1f633b6b38d8982896097da393dedd15fa9765a30161e53056a6f53002d0e42f6f0b71ffcbfc04165b78fafb865d25a593271f5b8031ec967754d4655822574bd59c461bc0f45997d544658843c396e005a4f54b5547be22ff17e1031f7f2224000000000000c35000015fc200000000050000047602015c3b570f9c00000002015c3b5738a7010100ab99e440733a747dfa9f3325e541b24f500831d332c551c2d10a1b65064824854343d741aaf59500733a747dfa9f3325e541b24f500831d332c551c2d10a1b65064824854343d7410120f372dc9d5e2a0d9683ad83874508ae193f2b8d1c9735ce0ee49e29b6260b02fb98ddaecc0af37a69744a9d2919b66ac376d52210f705579669e11d7bd8b84a1998bd9c82ca16bb5ebaffb872112128e1749edb33b912bd144d6bdbdc175d0802015c3b579215020100818afffca610e7511e875844ad95f767384fd79396439c636e2e76c57424b05867456b7ab62fa7d610330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3f818afffca610330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3f0108f5380fafc0df6dec81132f24d8bbdc20bd321a677e84b1473f91e01bd4386799e9db891e03fea16d02ec3d2e649bfc3624409948973a938e0c4f2c8d57ca13674c6c45b99ab9ebc5d2bab7ba667f92bb1624f077525d6dfaaafd23edb7850f012c94f2bbe49899679c54482eba49bf1d024476845e478f9cce3238f612edd7616f95ca30231c35c6c5c96ed2603383099f648c16b504445e77ec94bc838e2a3e99787424970e6e9434eadd7c47c53103684702dec2e25aba7f2872df73b6ad0702015c3b5793cc020100dd8c94c700b8a8dc17e92a5d6f0accdf32cbad120838fea4fe8d2437ca1b9f0d46ca08a82ca7d610330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3fdd8c94c700330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3f01a36df6eee4bf43dca30bd01dc9b4ce2b32fcd44e5f2751fae79164fdabeef913c6c2d074a9865801160c1662560e2c9050269cb7931584bc184c6240758869623fb528aa7a7123ee6385073bcc0a67af4117544245b643a3ab08f66c4ad18f0d012c94f2bbe49899679c54482eba49bf1d024476845e478f9cce3238f612edd761965b5760236470938afa385555c5c398df23c08fbce69612a05393a736cd4c14478557019386b1c15230821b4e9db1d0404603c427e06eb05e101a1718011404000002015c3b5a24e102010081a5dadff81c9e2661b5bb8ade3deb2bb7e6b078e5c5e0398ea2b62c2c4cc3c4b3f1b3b5d7a2a7d610330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3f81a5dadff81c330fd717584445ac866dc2facd8b856e63bdb8b15b5ed46c0b053b2c6c5c5c3f0141fb6c31249ece9e18b1f7d99285f7002e5cabeb7580fa200ed3630b3d5049feab6768b9e794516ab1cc49b816a680396b7f8b19e213d729c725f298a81e0b670baa8c31493f8ed691da450abb5acddf9a9e32286c4f1e20c07ce2840235880b012c94f2bbe49899679c54482eba49bf1d024476845e478f9cce3238f612edd761bcf6f84ceb25d91848adcf99115a51419dec155b7f09ae4b09aff19a864c2ab45e089ac6794231c2c37b1cef98875906174e8fee9a07d4362ce30fe2bc9073070000000000000000" + t2.KeyMR = "ac2919000a514726e08b961a8b2443072cb37492a6c88eb81926d84ea189d2e8" + t2.Hash = "35ac556392f934d702605eac3dac3138cdc134e3f188392afb550ee797d377f9" + ts = append(ts, t2) + + for _, tBlock := range ts { + rawStr := tBlock.Raw + raw, err := hex.DecodeString(rawStr) + if err != nil { + t.Errorf("%v", err) + } - f := new(FBlock) - rest, err := f.UnmarshalBinaryData(raw) - if err != nil { - t.Errorf("%v", err) - } - if len(rest) > 0 { - t.Errorf("Returned too much data - %x", rest) - } + f := new(FBlock) + rest, err := f.UnmarshalBinaryData(raw) + if err != nil { + t.Errorf("%v", err) + } + if len(rest) > 0 { + t.Errorf("Returned too much data - %x", rest) + } - b, err := f.MarshalBinary() - if err != nil { - t.Errorf("%v", err) - } + b, err := f.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } - if primitives.AreBytesEqual(raw, b) == false { - t.Errorf("Marshalled bytes are not equal - %x vs %x", raw, b) - } + if primitives.AreBytesEqual(raw, b) == false { + t.Errorf("Marshalled bytes are not equal - %x vs %x", raw, b) + } - //f.CalculateHashes() + //f.CalculateHashes() - if f.DatabasePrimaryIndex().String() != "aa100f203f159e4369081bb366f6816b302387ec19a4f8b9c98495d97fbe3527" { - t.Errorf("Wrong PrimaryIndex - %v vs %v", f.DatabasePrimaryIndex().String(), "aa100f203f159e4369081bb366f6816b302387ec19a4f8b9c98495d97fbe3527") - } - if f.DatabaseSecondaryIndex().String() != "5810ed83155dfb7b6039323b8a5572cd03166a37d1c3e86d4538c99907a81757" { - t.Errorf("Wrong SecondaryIndex - %v vs %v", f.DatabaseSecondaryIndex().String(), "5810ed83155dfb7b6039323b8a5572cd03166a37d1c3e86d4538c99907a81757") - } + if f.DatabasePrimaryIndex().String() != tBlock.KeyMR { + t.Errorf("Wrong PrimaryIndex - %v vs %v", f.DatabasePrimaryIndex().String(), tBlock.KeyMR) + } + if f.DatabaseSecondaryIndex().String() != tBlock.Hash { + t.Errorf("Wrong SecondaryIndex - %v vs %v", f.DatabaseSecondaryIndex().String(), tBlock.Hash) + } - err = f.Validate() - if err != nil { - t.Errorf("%v", err) + err = f.Validate() + if err != nil { + t.Errorf("%v", err) + } } } diff --git a/common/factoid/signature.go b/common/factoid/signature.go index 23ea4fa35d..2d9e385278 100644 --- a/common/factoid/signature.go +++ b/common/factoid/signature.go @@ -72,11 +72,8 @@ func (s *FactoidSignature) GetSignature() *[constants.SIGNATURE_LENGTH]byte { } func (s FactoidSignature) MarshalBinary() ([]byte, error) { - var out primitives.Buffer - - out.Write(s.Signature[:]) - - return out.DeepCopyBytes(), nil + buf := primitives.NewBuffer(s.Signature[:]) + return buf.DeepCopyBytes(), nil } func (s FactoidSignature) CustomMarshalText() ([]byte, error) { diff --git a/common/factoid/signatureblock.go b/common/factoid/signatureblock.go index 652b17d539..eb076fca78 100644 --- a/common/factoid/signatureblock.go +++ b/common/factoid/signatureblock.go @@ -5,8 +5,6 @@ package factoid import ( - "fmt" - "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/primitives" ) @@ -90,17 +88,14 @@ func (s SignatureBlock) GetSignatures() []interfaces.ISignature { } func (a SignatureBlock) MarshalBinary() ([]byte, error) { - var out primitives.Buffer - + buf := primitives.NewBuffer(nil) for _, sig := range a.GetSignatures() { - data, err := sig.MarshalBinary() + err := buf.PushBinaryMarshallable(sig) if err != nil { - return nil, fmt.Errorf("Signature failed to Marshal in RCD_1") + return nil, err } - out.Write(data) } - - return out.DeepCopyBytes(), nil + return buf.DeepCopyBytes(), nil } func (s SignatureBlock) CustomMarshalText() ([]byte, error) { @@ -121,15 +116,15 @@ func (s SignatureBlock) CustomMarshalText() ([]byte, error) { return out.DeepCopyBytes(), nil } -func (s *SignatureBlock) UnmarshalBinaryData(data []byte) (newData []byte, err error) { +func (s *SignatureBlock) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) s.Signatures = make([]interfaces.ISignature, 1) s.Signatures[0] = new(FactoidSignature) - data, err = s.Signatures[0].UnmarshalBinaryData(data) + err := buf.PopBinaryMarshallable(s.Signatures[0]) if err != nil { - return nil, fmt.Errorf("Failure to unmarshal Signature") + return nil, err } - - return data, nil + return buf.DeepCopyBytes(), nil } func NewSingleSignatureBlock(priv, data []byte) *SignatureBlock { diff --git a/common/factoid/transaction.go b/common/factoid/transaction.go index 2db12c009e..e868e9efdf 100644 --- a/common/factoid/transaction.go +++ b/common/factoid/transaction.go @@ -5,7 +5,6 @@ package factoid import ( - "encoding/binary" "fmt" "runtime/debug" "time" @@ -425,51 +424,61 @@ func (t *Transaction) GetRCD(i int) (interfaces.IRCD, error) { // UnmarshalBinary assumes that the Binary is all good. We do error // out if there isn't enough data, or the transaction is too large. -func (t *Transaction) UnmarshalBinaryData(data []byte) (newData []byte, err error) { - // To catch memory errors, I capture the panic and turn it into - // a reported error. - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Error unmarshalling transaction: %v", r) - } - }() +func (t *Transaction) UnmarshalBinaryData(data []byte) ([]byte, error) { + buf := primitives.NewBuffer(data) - v, data := primitives.DecodeVarInt(data) + v, err := buf.PopVarInt() + if err != nil { + return nil, err + } if v != t.GetVersion() { return nil, fmt.Errorf("Wrong Transaction Version encountered. Expected %v and found %v", t.GetVersion(), v) } - hd, data := binary.BigEndian.Uint32(data[:]), data[4:] - ld, data := binary.BigEndian.Uint16(data[:]), data[2:] + + hd, err := buf.PopUInt32() + if err != nil { + return nil, err + } + ld, err := buf.PopUInt16() + if err != nil { + return nil, err + } t.MilliTimestamp = (uint64(hd) << 16) + uint64(ld) - numInputs := int(data[0]) - data = data[1:] - numOutputs := int(data[0]) - data = data[1:] - numOutECs := int(data[0]) - data = data[1:] + numInputs, err := buf.PopUInt8() + if err != nil { + return nil, err + } + numOutputs, err := buf.PopUInt8() + if err != nil { + return nil, err + } + numOutECs, err := buf.PopUInt8() + if err != nil { + return nil, err + } - t.Inputs = make([]interfaces.ITransAddress, numInputs, numInputs) - t.Outputs = make([]interfaces.ITransAddress, numOutputs, numOutputs) - t.OutECs = make([]interfaces.ITransAddress, numOutECs, numOutECs) + t.Inputs = make([]interfaces.ITransAddress, int(numInputs), int(numInputs)) + t.Outputs = make([]interfaces.ITransAddress, int(numOutputs), int(numOutputs)) + t.OutECs = make([]interfaces.ITransAddress, int(numOutECs), int(numOutECs)) for i, _ := range t.Inputs { t.Inputs[i] = new(TransAddress) - data, err = t.Inputs[i].UnmarshalBinaryData(data) - if err != nil || t.Inputs[i] == nil { + err = buf.PopBinaryMarshallable(t.Inputs[i]) + if err != nil { return nil, err } } for i, _ := range t.Outputs { t.Outputs[i] = new(TransAddress) - data, err = t.Outputs[i].UnmarshalBinaryData(data) + err = buf.PopBinaryMarshallable(t.Outputs[i]) if err != nil { return nil, err } } for i, _ := range t.OutECs { t.OutECs[i] = new(TransAddress) - data, err = t.OutECs[i].UnmarshalBinaryData(data) + err = buf.PopBinaryMarshallable(t.OutECs[i]) if err != nil { return nil, err } @@ -479,20 +488,23 @@ func (t *Transaction) UnmarshalBinaryData(data []byte) (newData []byte, err erro t.SigBlocks = make([]interfaces.ISignatureBlock, len(t.Inputs)) for i := 0; i < len(t.Inputs); i++ { - t.RCDs[i] = CreateRCD(data) - data, err = t.RCDs[i].UnmarshalBinaryData(data) + b, err := buf.PeekByte() + if err != nil { + return nil, err + } + t.RCDs[i] = CreateRCD([]byte{b}) + err = buf.PopBinaryMarshallable(t.RCDs[i]) if err != nil { return nil, err } - t.SigBlocks[i] = new(SignatureBlock) - data, err = t.SigBlocks[i].UnmarshalBinaryData(data) + err = buf.PopBinaryMarshallable(t.SigBlocks[i]) if err != nil { return nil, err } } - return data, nil + return buf.DeepCopyBytes(), nil } func (t *Transaction) UnmarshalBinary(data []byte) (err error) { @@ -502,65 +514,76 @@ func (t *Transaction) UnmarshalBinary(data []byte) (err error) { // This is what Gets Signed. Yet signature blocks are part of the transaction. // We don't include them here, and tack them on later. -func (t *Transaction) MarshalBinarySig() (newData []byte, err error) { - var out primitives.Buffer +func (t *Transaction) MarshalBinarySig() ([]byte, error) { + buf := primitives.NewBuffer(nil) - primitives.EncodeVarInt(&out, t.GetVersion()) + err := buf.PushVarInt(t.GetVersion()) + if err != nil { + return nil, err + } hd := uint32(t.MilliTimestamp >> 16) ld := uint16(t.MilliTimestamp & 0xFFFF) - binary.Write(&out, binary.BigEndian, uint32(hd)) - binary.Write(&out, binary.BigEndian, uint16(ld)) - out.WriteByte(byte(len(t.Inputs))) - out.WriteByte(byte(len(t.Outputs))) - out.WriteByte(byte(len(t.OutECs))) + err = buf.PushUInt32(hd) + if err != nil { + return nil, err + } + err = buf.PushUInt16(ld) + if err != nil { + return nil, err + } + + err = buf.PushByte(byte(len(t.Inputs))) + if err != nil { + return nil, err + } + err = buf.PushByte(byte(len(t.Outputs))) + if err != nil { + return nil, err + } + err = buf.PushByte(byte(len(t.OutECs))) + if err != nil { + return nil, err + } for _, input := range t.Inputs { - data, err := input.MarshalBinary() + err = buf.PushBinaryMarshallable(input) if err != nil { return nil, err } - out.Write(data) } - for _, output := range t.Outputs { - data, err := output.MarshalBinary() + err = buf.PushBinaryMarshallable(output) if err != nil { return nil, err } - out.Write(data) } - for _, outEC := range t.OutECs { - data, err := outEC.MarshalBinary() + err = buf.PushBinaryMarshallable(outEC) if err != nil { return nil, err } - out.Write(data) } - return out.DeepCopyBytes(), nil + return buf.DeepCopyBytes(), nil } // This just Marshals what gets signed, i.e. MarshalBinarySig(), then // Marshals the signatures and the RCDs for this transaction. func (t Transaction) MarshalBinary() ([]byte, error) { - var out primitives.Buffer - data, err := t.MarshalBinarySig() if err != nil { return nil, err } - out.Write(data) + buf := primitives.NewBuffer(data) for i, rcd := range t.RCDs { // Write the RCD - data, err := rcd.MarshalBinary() + err = buf.PushBinaryMarshallable(rcd) if err != nil { return nil, err } - out.Write(data) // Then write its signature blocks. This needs to be // reworked so we use the information from the RCD block @@ -570,14 +593,13 @@ func (t Transaction) MarshalBinary() ([]byte, error) { if len(t.SigBlocks) <= i { t.SigBlocks = append(t.SigBlocks, new(SignatureBlock)) } - data, err = t.SigBlocks[i].MarshalBinary() + err = buf.PushBinaryMarshallable(t.SigBlocks[i]) if err != nil { return nil, err } - out.Write(data) } - return out.DeepCopyBytes(), nil + return buf.DeepCopyBytes(), nil } // Helper function for building transactions. Add an input to diff --git a/common/factoid/transaddress.go b/common/factoid/transaddress.go index 9e1203aad5..a4021bdd20 100644 --- a/common/factoid/transaddress.go +++ b/common/factoid/transaddress.go @@ -73,31 +73,39 @@ func (t *TransAddress) IsSameAs(add interfaces.ITransAddress) bool { return true } -func (t *TransAddress) UnmarshalBinaryData(data []byte) (newData []byte, err error) { +func (t *TransAddress) UnmarshalBinaryData(data []byte) ([]byte, error) { if len(data) < 36 { return nil, fmt.Errorf("Data source too short to UnmarshalBinary() an address: %d", len(data)) } + buf := primitives.NewBuffer(data) + var err error - t.Amount, data = primitives.DecodeVarInt(data) - t.Address = new(Address) + t.Amount, err = buf.PopVarInt() + if err != nil { + return nil, err + } - data, err = t.Address.UnmarshalBinaryData(data) + t.Address = new(Address) + err = buf.PopBinaryMarshallable(t.Address) + if err != nil { + return nil, err + } - return data, err + return buf.DeepCopyBytes(), nil } // MarshalBinary. 'nuff said func (a TransAddress) MarshalBinary() ([]byte, error) { - var out primitives.Buffer - - err := primitives.EncodeVarInt(&out, a.Amount) + buf := primitives.NewBuffer(nil) + err := buf.PushVarInt(a.Amount) if err != nil { return nil, err } - data, err := a.Address.MarshalBinary() - out.Write(data) - - return out.DeepCopyBytes(), err + err = buf.PushBinaryMarshallable(a.Address) + if err != nil { + return nil, err + } + return buf.DeepCopyBytes(), nil } // Accessor. Default to a zero length string. This is a debug diff --git a/common/identity/authority_test.go b/common/identity/authority_test.go index 3c24a2de3c..47f875e530 100644 --- a/common/identity/authority_test.go +++ b/common/identity/authority_test.go @@ -86,3 +86,22 @@ func TestMarshalJSON(t *testing.T) { t.Errorf("Invalid json returned - %v vs %v", string(j), expected) } } + +func TestAuthorityMarshalUnmarshal(t *testing.T) { + for i := 0; i < 1000; i++ { + a := RandomAuthority() + + h, err := a.MarshalBinary() + if err != nil { + t.Errorf("%v", err) + } + a2 := new(Authority) + err = a2.UnmarshalBinary(h) + if err != nil { + t.Errorf("%v", err) + } + if a.IsSameAs(a2) == false { + t.Errorf("Authorities are not identical") + } + } +} diff --git a/common/identity/identity.go b/common/identity/identity.go index 24bcc9aa1f..aff9585ce5 100644 --- a/common/identity/identity.go +++ b/common/identity/identity.go @@ -279,6 +279,7 @@ func (e *Identity) UnmarshalBinaryData(p []byte) (newData []byte, err error) { e.AnchorKeys = append(e.AnchorKeys, ak) } + newData = buf.DeepCopyBytes() return } diff --git a/common/interfaces/timestamp.go b/common/interfaces/timestamp.go index 071e2b3e2b..0a95c9a66f 100644 --- a/common/interfaces/timestamp.go +++ b/common/interfaces/timestamp.go @@ -24,5 +24,6 @@ type Timestamp interface { GetTimeSecondsUInt32() uint32 MarshalBinary() ([]byte, error) String() string + UTCString() string IsSameAs(Timestamp) bool } diff --git a/common/primitives/buffer.go b/common/primitives/buffer.go index a2cd6a822c..f54e3559b3 100644 --- a/common/primitives/buffer.go +++ b/common/primitives/buffer.go @@ -22,11 +22,28 @@ func (b *Buffer) DeepCopyBytes() []byte { func NewBuffer(buf []byte) *Buffer { tmp := new(Buffer) - tmp.Buffer = *bytes.NewBuffer(buf) + c := make([]byte, len(buf)) + copy(c, buf) + tmp.Buffer = *bytes.NewBuffer(c) return tmp } +func (b *Buffer) PeekByte() (byte, error) { + by, err := b.ReadByte() + if err != nil { + return by, err + } + err = b.UnreadByte() + if err != nil { + return by, err + } + return by, nil +} + func (b *Buffer) PushBinaryMarshallable(bm interfaces.BinaryMarshallable) error { + if bm == nil { + return fmt.Errorf("BinaryMarshallable is nil") + } bin, err := bm.MarshalBinary() if err != nil { return err @@ -95,6 +112,31 @@ func (b *Buffer) PushInt64(i int64) error { return b.PushUInt64(uint64(i)) } +func (b *Buffer) PushUInt8(h uint8) error { + return b.PushByte(byte(h)) +} + +func (b *Buffer) PushUInt16(i uint16) error { + return binary.Write(b, binary.BigEndian, &i) +} + +func (b *Buffer) PopUInt16() (uint16, error) { + var i uint16 + err := binary.Read(b, binary.BigEndian, &i) + if err != nil { + return 0, err + } + return i, nil +} + +func (b *Buffer) PopUInt8() (uint8, error) { + h, err := b.PopByte() + if err != nil { + return 0, err + } + return uint8(h), nil +} + func (b *Buffer) PushInt(i int) error { return b.PushInt64(int64(i)) } @@ -168,6 +210,9 @@ func (b *Buffer) PopBytes() ([]byte, error) { h := b.DeepCopyBytes() l, rest := DecodeVarInt(h) + if int(l) > len(rest) { + return nil, fmt.Errorf("End of buffer") + } answer := make([]byte, int(l)) copy(answer, rest) remainder := rest[int(l):] @@ -198,6 +243,9 @@ func (b *Buffer) Pop(h []byte) error { } func (b *Buffer) PopBinaryMarshallable(dst interfaces.BinaryMarshallable) error { + if dst == nil { + return fmt.Errorf("Destination is nil") + } h := b.DeepCopyBytes() rest, err := dst.UnmarshalBinaryData(h) if err != nil { diff --git a/common/primitives/timestamp.go b/common/primitives/timestamp.go index 9e91bcbd4e..38e05e8002 100644 --- a/common/primitives/timestamp.go +++ b/common/primitives/timestamp.go @@ -128,3 +128,7 @@ func (t *Timestamp) MarshalBinary() ([]byte, error) { func (t *Timestamp) String() string { return t.GetTime().Format("2006-01-02 15:04:05") } + +func (t *Timestamp) UTCString() string { + return t.GetTime().UTC().Format("2006-01-02 15:04:05") +} diff --git a/common/primitives/timestamp_test.go b/common/primitives/timestamp_test.go index 1f2fcca038..e9ed469ddb 100644 --- a/common/primitives/timestamp_test.go +++ b/common/primitives/timestamp_test.go @@ -191,5 +191,8 @@ func TestTimestampMisc(t *testing.T) { if ts.GetTimeMilliUInt64() != ts5.GetTimeMilliUInt64() { t.Errorf("Timestamps are not identical") } + if ts.UTCString() != ts5.UTCString() { + t.Errorf("Timestamps are not identical") + } } } diff --git a/controlPanel/controlPanel.go b/controlPanel/controlPanel.go index 4cf50e34c1..91f69fdb4c 100644 --- a/controlPanel/controlPanel.go +++ b/controlPanel/controlPanel.go @@ -583,23 +583,19 @@ func getRecentTransactions(time.Time) { if fTrans.TotalInputs == 0 { continue } - has := false - for _, trans := range RecentTransactions.FactoidTransactions { - if fTrans.TxID == trans.TxID { - has = true - break + txhash, err := primitives.HexToHash(fTrans.TxID) + if err == nil { + if !RecentTransactions.ContainsTrans(txhash) { + RecentTransactions.FactoidTransactions = append(RecentTransactions.FactoidTransactions, struct { + TxID string + Hash string + TotalInput string + Status string + TotalInputs int + TotalOutputs int + }{fTrans.TxID, fTrans.Hash, fTrans.TotalInput, "Processing", fTrans.TotalInputs, fTrans.TotalOutputs}) } } - if !has { - RecentTransactions.FactoidTransactions = append(RecentTransactions.FactoidTransactions, struct { - TxID string - Hash string - TotalInput string - Status string - TotalInputs int - TotalOutputs int - }{fTrans.TxID, fTrans.Hash, fTrans.TotalInput, "Processing", fTrans.TotalInputs, fTrans.TotalOutputs}) - } } DisplayStateMutex.RUnlock() @@ -626,22 +622,7 @@ func getRecentTransactions(time.Time) { totalOutputs := len(trans.GetECOutputs()) totalOutputs = totalOutputs + len(trans.GetOutputs()) inputStr := fmt.Sprintf("%f", float64(input)/1e8) - has := false - for i, fact := range RecentTransactions.FactoidTransactions { - if fact.TxID == trans.GetHash().String() { - RecentTransactions.FactoidTransactions[i] = struct { - TxID string - Hash string - TotalInput string - Status string - TotalInputs int - TotalOutputs int - }{trans.GetSigHash().String(), trans.GetHash().String(), inputStr, "Confirmed", totalInputs, totalOutputs} - has = true - break - } - } - if !has { + if !RecentTransactions.ContainsTrans(trans.GetHash()) { RecentTransactions.FactoidTransactions = append(RecentTransactions.FactoidTransactions, struct { TxID string Hash string diff --git a/controlPanel/controlPanel_test.go b/controlPanel/controlPanel_test.go index 620241924b..5d7464b98f 100644 --- a/controlPanel/controlPanel_test.go +++ b/controlPanel/controlPanel_test.go @@ -8,6 +8,8 @@ import ( . "github.com/FactomProject/factomd/controlPanel" "github.com/FactomProject/factomd/p2p" //"github.com/FactomProject/factomd/state" + "github.com/FactomProject/factomd/common/primitives" + //"github.com/FactomProject/factomd/common/primitives/random" . "github.com/FactomProject/factomd/testHelper" ) @@ -16,6 +18,43 @@ var _ = fmt.Sprintf("") // Enable for long test var LongTest bool = false +func TestFactoidHas(t *testing.T) { + rc := new(LastDirectoryBlockTransactions) + rc.FactoidTransactions = append(rc.FactoidTransactions) + for i := 0; i < 10; i++ { + addTrans(rc) + } + + for i := 0; i < len(rc.FactoidTransactions); i++ { + h, _ := primitives.HexToHash(rc.FactoidTransactions[i].TxID) + if !rc.ContainsTrans(h) { + t.Error("This should be true") + } + } + + for i := 0; i < len(rc.Entries); i++ { + h, _ := primitives.HexToHash(rc.Entries[i].Hash) + if !rc.ContainsEntry(h) { + t.Error("This should be true") + } + } +} + +func addTrans(rc *LastDirectoryBlockTransactions) { + rc.FactoidTransactions = append(rc.FactoidTransactions, struct { + TxID string + Hash string + TotalInput string + Status string + TotalInputs int + TotalOutputs int + }{primitives.RandomHash().String(), primitives.RandomHash().String(), "1", "Confirmed", 1, 1}) + + e := new(EntryHolder) + e.Hash = primitives.RandomHash().String() + rc.Entries = append(rc.Entries, *e) +} + func TestControlPanel(t *testing.T) { if LongTest { var i uint32 diff --git a/database/boltdb/boltdb_test.go b/database/boltdb/boltdb_test.go index 6e82b97678..73a464859c 100644 --- a/database/boltdb/boltdb_test.go +++ b/database/boltdb/boltdb_test.go @@ -10,8 +10,11 @@ import ( "testing" "github.com/FactomProject/factomd/common/interfaces" + "github.com/FactomProject/factomd/common/primitives" "github.com/FactomProject/factomd/common/primitives/random" . "github.com/FactomProject/factomd/database/boltdb" + "github.com/FactomProject/factomd/database/databaseOverlay" + "github.com/FactomProject/factomd/testHelper" ) type TestData struct { @@ -192,3 +195,29 @@ func TestDoesKeyExist(t *testing.T) { } } } + +func TestGetAll(t *testing.T) { + m := NewBoltDB(nil, dbFilename) + defer CleanupTest(t, m) + + dbo := databaseOverlay.NewOverlay(m) + testHelper.PopulateTestDatabaseOverlay(dbo) + + _, keys, err := dbo.GetAll(databaseOverlay.INCLUDED_IN, primitives.NewZeroHash()) + if err != nil { + t.Errorf("%v", err) + } + if len(keys) != 150 { + t.Errorf("Invalid amount of keys returned - expected 150, got %v", len(keys)) + } + for i := range keys { + for j := i + 1; j < len(keys); j++ { + if primitives.AreBytesEqual(keys[i], keys[j]) { + t.Errorf("Key %v is equal to key %v - %x", i, j, keys[i]) + } + } + if len(keys[i]) != 32 { + t.Errorf("Wrong key length at index %v - %v", i, len(keys[i])) + } + } +} diff --git a/database/databaseOverlay/dblock_test.go b/database/databaseOverlay/dblock_test.go index 7626f333f9..db8ba3ba81 100644 --- a/database/databaseOverlay/dblock_test.go +++ b/database/databaseOverlay/dblock_test.go @@ -18,7 +18,8 @@ import ( ) func TestSaveLoadDBlockHead(t *testing.T) { - b1 := testHelper.CreateTestDirectoryBlock(nil) + blocks := testHelper.CreateTestBlockSet(nil) + b1 := blocks.DBlock dbo := NewOverlay(new(mapdb.MapDB)) defer dbo.Close() @@ -49,7 +50,8 @@ func TestSaveLoadDBlockHead(t *testing.T) { t.Error("Blocks are not equal") } - b2 := testHelper.CreateTestDirectoryBlock(b1) + blocks = testHelper.CreateTestBlockSet(blocks) + b2 := blocks.DBlock err = dbo.SaveDirectoryBlockHead(b2) if err != nil { @@ -80,14 +82,14 @@ func TestSaveLoadDBlockHead(t *testing.T) { func TestSaveLoadDBlockChain(t *testing.T) { blocks := []*DirectoryBlock{} max := 10 - var prev *DirectoryBlock = nil + var prev *testHelper.BlockSet = nil dbo := NewOverlay(new(mapdb.MapDB)) defer dbo.Close() for i := 0; i < max; i++ { - prev = testHelper.CreateTestDirectoryBlock(prev) - blocks = append(blocks, prev) - err := dbo.SaveDirectoryBlockHead(prev) + prev = testHelper.CreateTestBlockSet(prev) + blocks = append(blocks, prev.DBlock) + err := dbo.SaveDirectoryBlockHead(prev.DBlock) if err != nil { t.Error(err) } diff --git a/database/databaseOverlay/overlay_test.go b/database/databaseOverlay/overlay_test.go index cda8ed2472..1547b38779 100644 --- a/database/databaseOverlay/overlay_test.go +++ b/database/databaseOverlay/overlay_test.go @@ -586,22 +586,20 @@ func TestDoesKeyExist(t *testing.T) { } func TestFetchAllBlockKeysFromBucket(t *testing.T) { - dbo := createOverlay() - defer dbo.Close() - - obj := NewDBTestObject() - bucket := []byte{0x01} + dbo := testHelper.CreateAndPopulateTestDatabaseOverlay() - err := dbo.Insert(bucket, obj) + _, keys, err := dbo.GetAll(INCLUDED_IN, primitives.NewZeroHash()) if err != nil { t.Errorf("%v", err) } - - keys, err := dbo.FetchAllBlockKeysFromBucket(bucket) - if err != nil { - t.Errorf("%v", err) + if len(keys) != 150 { + t.Errorf("Invalid amount of keys returned - expected 150, got %v", len(keys)) } - if len(keys) == 0 { - t.Errorf("No keys returned") + for i := range keys { + for j := i + 1; j < len(keys); j++ { + if primitives.AreBytesEqual(keys[i], keys[j]) { + t.Errorf("Key %v is equal to key %v - %x", i, j, keys[i]) + } + } } } diff --git a/database/leveldb/leveldb.go b/database/leveldb/leveldb.go index 96e011b03f..9593480438 100644 --- a/database/leveldb/leveldb.go +++ b/database/leveldb/leveldb.go @@ -242,7 +242,9 @@ func (db *LevelDB) GetAll(bucket []byte, sample interfaces.BinaryMarshallableAnd if err != nil { return nil, nil, err } - keys = append(keys, iter.Key()[len(ldbKey):]) + k := make([]byte, len(iter.Key())-len(ldbKey)) + copy(k, iter.Key()[len(ldbKey):]) + keys = append(keys, k) answer = append(answer, tmp) } iter.Release() diff --git a/database/leveldb/leveldb_test.go b/database/leveldb/leveldb_test.go index 9706862924..b1c5311e87 100644 --- a/database/leveldb/leveldb_test.go +++ b/database/leveldb/leveldb_test.go @@ -10,8 +10,11 @@ import ( "testing" "github.com/FactomProject/factomd/common/interfaces" + "github.com/FactomProject/factomd/common/primitives" "github.com/FactomProject/factomd/common/primitives/random" + "github.com/FactomProject/factomd/database/databaseOverlay" . "github.com/FactomProject/factomd/database/leveldb" + "github.com/FactomProject/factomd/testHelper" ) type TestData struct { @@ -201,3 +204,32 @@ func TestDoesKeyExist(t *testing.T) { } } } + +func TestGetAll(t *testing.T) { + m, err := NewLevelDB(dbFilename, true) + if err != nil { + t.Errorf("%v", err) + } + defer CleanupTest(t, m) + + dbo := databaseOverlay.NewOverlay(m) + testHelper.PopulateTestDatabaseOverlay(dbo) + + _, keys, err := dbo.GetAll(databaseOverlay.INCLUDED_IN, primitives.NewZeroHash()) + if err != nil { + t.Errorf("%v", err) + } + if len(keys) != 150 { + t.Errorf("Invalid amount of keys returned - expected 150, got %v", len(keys)) + } + for i := range keys { + for j := i + 1; j < len(keys); j++ { + if primitives.AreBytesEqual(keys[i], keys[j]) { + t.Errorf("Key %v is equal to key %v - %x", i, j, keys[i]) + } + } + if len(keys[i]) != 32 { + t.Errorf("Wrong key length at index %v - %v", i, len(keys[i])) + } + } +} diff --git a/database/mapdb/mapdb_test.go b/database/mapdb/mapdb_test.go index 217cc713bd..b274069440 100644 --- a/database/mapdb/mapdb_test.go +++ b/database/mapdb/mapdb_test.go @@ -7,8 +7,11 @@ import ( "time" "github.com/FactomProject/factomd/common/interfaces" + "github.com/FactomProject/factomd/common/primitives" "github.com/FactomProject/factomd/common/primitives/random" + "github.com/FactomProject/factomd/database/databaseOverlay" . "github.com/FactomProject/factomd/database/mapdb" + "github.com/FactomProject/factomd/testHelper" ) type TestData struct { @@ -219,3 +222,28 @@ func TestDoesKeyExist(t *testing.T) { } } } + +func TestGetAll(t *testing.T) { + m := new(MapDB) + + dbo := databaseOverlay.NewOverlay(m) + testHelper.PopulateTestDatabaseOverlay(dbo) + + _, keys, err := dbo.GetAll(databaseOverlay.INCLUDED_IN, primitives.NewZeroHash()) + if err != nil { + t.Errorf("%v", err) + } + if len(keys) != 150 { + t.Errorf("Invalid amount of keys returned - expected 150, got %v", len(keys)) + } + for i := range keys { + for j := i + 1; j < len(keys); j++ { + if primitives.AreBytesEqual(keys[i], keys[j]) { + t.Errorf("Key %v is equal to key %v - %x", i, j, keys[i]) + } + } + if len(keys[i]) != 32 { + t.Errorf("Wrong key length at index %v - %v", i, len(keys[i])) + } + } +} diff --git a/engine/NetStart.go b/engine/NetStart.go index 6129dd69c4..0ab385e5de 100644 --- a/engine/NetStart.go +++ b/engine/NetStart.go @@ -83,6 +83,8 @@ func NetStart(s *state.State) { fastPtr := flag.Bool("fast", true, "If true, factomd will fast-boot from a file.") fastLocationPtr := flag.String("fastlocation", "", "Directory to put the fast-boot file in.") memProfileRate := flag.Int("mpr", 512*1024, "Set the Memory Profile Rate to update profiling per X bytes allocated. Default 512K, set to 1 to profile everything, 0 to disable.") + logLvlPtr := flag.String("loglvl", "none", "Set log level to either: debug, info, notice, warning, error, critical, alert, emergency or none") + logSTDOutPtr := flag.Bool("logstdout", false, "Use to set logging to stdout") flag.Parse() @@ -123,6 +125,8 @@ func NetStart(s *state.State) { factomdTLS := *factomdTLSflag factomdLocations := *factomdLocationsflag fast := *fastPtr + logLvl := *logLvlPtr + logSTDOut := *logSTDOutPtr messages.AckBalanceHash = ackbalanceHash // Must add the prefix before loading the configuration. @@ -134,6 +138,11 @@ func NetStart(s *state.State) { s.TimeOffset = primitives.NewTimestampFromMilliseconds(uint64(timeOffset)) s.StartDelayLimit = startDelay * 1000 s.Journaling = journaling + s.LogLevel = logLvl + + if logSTDOut { + s.LogPath = "stdout" + } // Set the wait for entries flag s.WaitForEntries = waitEntries @@ -369,6 +378,8 @@ func NetStart(s *state.State) { SeedURL: seedURL, SpecialPeers: specialPeers, ConnectionMetricsChannel: connectionMetricsChannel, + LogPath: s.LogPath, + LogLevel: s.LogLevel, } p2pNetwork = new(p2p.Controller).Init(ci) fnodes[0].State.NetworkControler = p2pNetwork diff --git a/log/log_test.go b/log/log_test.go new file mode 100644 index 0000000000..c12c281f59 --- /dev/null +++ b/log/log_test.go @@ -0,0 +1,174 @@ +package log_test + +import ( + "bytes" + "fmt" + "testing" + + . "github.com/FactomProject/factomd/log" +) + +var _ = fmt.Println + +func TestLog(t *testing.T) { +} + +func TestBadNew(t *testing.T) { + l := New(nil, "not_allowed", "testing") + if l.Level() != WarningLvl { + t.Error("Should be set to warning") + } +} + +func TestNew(t *testing.T) { + buf := new(bytes.Buffer) + + floggers := make([]*FLogger, 0) + lDebug := New(buf, "debug", "testing") + linfo := New(buf, "info", "testing") + lnotice := New(buf, "notice", "testing") + lwarning := New(buf, "warning", "testing") + lerror := New(buf, "error", "testing") + lcritical := New(buf, "critical", "testing") + lalert := New(buf, "alert", "testing") + lemergency := New(buf, "emergency", "testing") + lnone := New(buf, "none", "testing") + + floggers = append(floggers, lDebug) + floggers = append(floggers, linfo) + floggers = append(floggers, lnotice) + floggers = append(floggers, lwarning) + floggers = append(floggers, lerror) + floggers = append(floggers, lcritical) + floggers = append(floggers, lalert) + floggers = append(floggers, lemergency) + floggers = append(floggers, lnone) + + for _, f := range floggers { + if !check(f, buf, DebugLvl) { + t.Error("Debug level not working") + } + if !check(f, buf, InfoLvl) { + t.Error("Info level not working") + } + if !check(f, buf, NoticeLvl) { + t.Error("Notice level not working") + } + if !check(f, buf, WarningLvl) { + t.Error("Warning level not working") + } + if !check(f, buf, ErrorLvl) { + t.Error("Error level not working") + } + } +} + +func check(f *FLogger, out *bytes.Buffer, lvl Level) bool { + if !checkLevel(f, out, lvl) { + return false + } + if !checkLevelf(f, out, lvl) { + return false + } + return true +} + +func checkLevel(f *FLogger, out *bytes.Buffer, lvl Level) bool { + pre := out.Len() + + switch lvl { + case DebugLvl: + f.Debug("Test") + case InfoLvl: + f.Info("Test") + case NoticeLvl: + f.Notice("Test") + case WarningLvl: + f.Warning("Test") + case ErrorLvl: + f.Error("Test") + case CriticalLvl: + case AlertLvl: + case EmergencyLvl: + } + + post := out.Len() + if f.Level() == None { + if pre != post { + return false + } + } + + if f.Level() >= lvl { + if post <= pre { + return false + } + } else { + if post > pre { + return false + } + } + return true +} + +func checkLevelf(f *FLogger, out *bytes.Buffer, lvl Level) bool { + pre := out.Len() + + switch lvl { + case DebugLvl: + f.Debugf("Test") + case InfoLvl: + f.Infof("Test") + case NoticeLvl: + f.Noticef("Test") + case WarningLvl: + f.Warningf("Test") + case ErrorLvl: + f.Errorf("Test") + case CriticalLvl: + case AlertLvl: + case EmergencyLvl: + } + + post := out.Len() + if f.Level() == None { + if pre != post { + return false + } + } + + if f.Level() >= lvl { + if post <= pre { + return false + } + } else { + if post > pre { + return false + } + } + return true +} + +func BenchmarkZeroFormat(b *testing.B) { + buf := new(bytes.Buffer) + l := New(buf, "debug", "testing") + doBenchmark(b, l, "zero") +} + +func BenchmarkSingleFormat(b *testing.B) { + buf := new(bytes.Buffer) + l := New(buf, "debug", "testing") + doBenchmark(b, l, "%s", "single") +} + +func BenchmarkDoubleFormat(b *testing.B) { + buf := new(bytes.Buffer) + l := New(buf, "debug", "testing") + doBenchmark(b, l, "%s, %s", "single", "double") +} + +func doBenchmark(b *testing.B, logger *FLogger, format string, args ...interface{}) { + for i := 0; i < b.N; i++ { + logger.Debugf(format, args) + } +} diff --git a/logger/logger.go b/log/logger.go similarity index 58% rename from logger/logger.go rename to log/logger.go index 96dcb66fff..9d14b2d330 100644 --- a/logger/logger.go +++ b/log/logger.go @@ -5,7 +5,7 @@ // logger is based on github.com/alexcesaro/log and // github.com/alexcesaro/log/golog (MIT License) -package logger +package log import ( "fmt" @@ -18,16 +18,17 @@ import ( // severities described in RFC 5424 and none. type Level int8 +// Levels const ( - None Level = iota - 1 - Emergency - Alert - Critical - Error - Warning - Notice - Info - Debug + None Level = iota - 1 + EmergencyLvl // 1 + AlertLvl // 2 + CriticalLvl // 3 + ErrorLvl // 4 + WarningLvl // 5 + NoticeLvl // 6 + InfoLvl // 7 + DebugLvl // 8 ) // A FLogger represents an active logging object that generates lines of output @@ -38,11 +39,18 @@ type FLogger struct { prefix string } +// NewLogFromConfig outputs logs to a file given by logpath func NewLogFromConfig(logPath, logLevel, prefix string) *FLogger { - logFile, _ := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660) + var logFile io.Writer + if logPath == "stdout" { + logFile = os.Stdout + } else { + logFile, _ = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660) + } return New(logFile, logLevel, prefix) } +// New makes a new logger with a given level func New(w io.Writer, level, prefix string) *FLogger { return &FLogger{ out: w, @@ -51,97 +59,127 @@ func New(w io.Writer, level, prefix string) *FLogger { } } -// Get the current log level +// Level Get the current log level func (logger *FLogger) Level() (level Level) { return logger.level } +// Println is implemented so this logger shares the same functions as "log" +func (logger *FLogger) Println(args ...interface{}) { + logger.write(InfoLvl, args...) +} + +// Printf is implemented so this logger shares the same functions as "log" +func (logger *FLogger) Printf(format string, args ...interface{}) { + logger.write(InfoLvl, fmt.Sprintf(format, args...)) +} + // Emergency logs with an emergency level and exits the program. func (logger *FLogger) Emergency(args ...interface{}) { - logger.write(Emergency, args...) + logger.write(EmergencyLvl, args...) } // Emergencyf logs with an emergency level and exits the program. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Emergencyf(format string, args ...interface{}) { - logger.write(Emergency, fmt.Sprintf(format, args...)) + logger.write(EmergencyLvl, fmt.Sprintf(format, args...)) } // Alert logs with an alert level and exits the program. func (logger *FLogger) Alert(args ...interface{}) { - logger.write(Alert, args...) + logger.write(AlertLvl, args...) } // Alertf logs with an alert level and exits the program. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Alertf(format string, args ...interface{}) { - logger.write(Alert, fmt.Sprintf(format, args...)) + logger.write(AlertLvl, fmt.Sprintf(format, args...)) } // Critical logs with a critical level and exits the program. func (logger *FLogger) Critical(args ...interface{}) { - logger.write(Critical, args...) + logger.write(CriticalLvl, args...) } // Criticalf logs with a critical level and exits the program. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Criticalf(format string, args ...interface{}) { - logger.write(Critical, fmt.Sprintf(format, args...)) + logger.write(CriticalLvl, fmt.Sprintf(format, args...)) } // Error logs with an error level. func (logger *FLogger) Error(args ...interface{}) { - logger.write(Error, args...) + logger.write(ErrorLvl, args...) } // Errorf logs with an error level. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Errorf(format string, args ...interface{}) { - logger.write(Error, fmt.Sprintf(format, args...)) + // Do not do overhead of formatting a string if not going to log + if ErrorLvl > logger.level { + return + } + logger.write(ErrorLvl, fmt.Sprintf(format, args...)) } // Warning logs with a warning level. func (logger *FLogger) Warning(args ...interface{}) { - logger.write(Warning, args...) + logger.write(WarningLvl, args...) } // Warningf logs with a warning level. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Warningf(format string, args ...interface{}) { - logger.write(Warning, fmt.Sprintf(format, args...)) + // Do not do overhead of formatting a string if not going to log + if WarningLvl > logger.level { + return + } + logger.write(WarningLvl, fmt.Sprintf(format, args...)) } // Notice logs with a notice level. func (logger *FLogger) Notice(args ...interface{}) { - logger.write(Notice, args...) + logger.write(NoticeLvl, args...) } // Noticef logs with a notice level. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Noticef(format string, args ...interface{}) { - logger.write(Notice, fmt.Sprintf(format, args...)) + // Do not do overhead of formatting a string if not going to log + if NoticeLvl > logger.level { + return + } + logger.write(NoticeLvl, fmt.Sprintf(format, args...)) } // Info logs with an info level. func (logger *FLogger) Info(args ...interface{}) { - logger.write(Info, args...) + logger.write(InfoLvl, args...) } // Infof logs with an info level. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Infof(format string, args ...interface{}) { - logger.write(Info, fmt.Sprintf(format, args...)) + // Do not do overhead of formatting a string if not going to log + if InfoLvl > logger.level { + return + } + logger.write(InfoLvl, fmt.Sprintf(format, args...)) } // Debug logs with a debug level. func (logger *FLogger) Debug(args ...interface{}) { - logger.write(Debug, args...) + logger.write(DebugLvl, args...) } // Debugf logs with a debug level. // Arguments are handled in the manner of fmt.Printf. func (logger *FLogger) Debugf(format string, args ...interface{}) { - logger.write(Debug, fmt.Sprintf(format, args...)) + // Do not do overhead of formatting a string if not going to log + if DebugLvl > logger.level { + return + } + logger.write(DebugLvl, fmt.Sprintf(format, args...)) } // write outputs to the FLogger.out based on the FLogger.level and calls os.Exit @@ -152,48 +190,52 @@ func (logger *FLogger) write(level Level, args ...interface{}) { } l := fmt.Sprint(args...) // get string for formatting - fmt.Fprintf(logger.out, "%s [%s] %s: %s\n", time.Now().Format(time.RFC3339), levelPrefix[level], logger.prefix, l) + if level == DebugLvl { + fmt.Fprintf(logger.out, "%s [%s] %s: %s\n", debugPrefix(), levelPrefix[level], logger.prefix, l) + } else { + fmt.Fprintf(logger.out, "%s [%s] %s: %s\n", time.Now().Format(time.RFC3339), levelPrefix[level], logger.prefix, l) + } - if level <= Critical { + if level <= CriticalLvl { os.Exit(1) } } var levelPrefix = map[Level]string{ - Emergency: "EMERGENCY", - Alert: "ALERT", - Critical: "CRITICAL", - Error: "ERROR", - Warning: "WARNING", - Notice: "NOTICE", - Info: "INFO", - Debug: "DEBUG", + EmergencyLvl: "EMERGENCY", + AlertLvl: "ALERT", + CriticalLvl: "CRITICAL", + ErrorLvl: "ERROR", + WarningLvl: "WARNING", + NoticeLvl: "NOTICE", + InfoLvl: "INFO", + DebugLvl: "DEBUG", } func levelFromString(levelName string) (level Level) { switch levelName { case "debug": - level = Debug + level = DebugLvl case "info": - level = Info + level = InfoLvl case "notice": - level = Notice + level = NoticeLvl case "warning": - level = Warning + level = WarningLvl case "error": - level = Error + level = ErrorLvl case "critical": - level = Critical + level = CriticalLvl case "alert": - level = Alert + level = AlertLvl case "emergency": - level = Emergency + level = EmergencyLvl case "none": level = None default: fmt.Fprintf(os.Stderr, "Invalid level value %q, allowed values are: debug, info, notice, warning, error, critical, alert, emergency and none\n", levelName) fmt.Fprintln(os.Stderr, "Using log level of warning") - level = Warning + level = WarningLvl } return } diff --git a/logger/logger_test.go b/logger/logger_test.go deleted file mode 100644 index 4a6a4c2aca..0000000000 --- a/logger/logger_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package logger - -import ( - "bytes" - "fmt" - "testing" -) - -func TestNew(t *testing.T) { - var buf bytes.Buffer - - name := "Michael" - - logger := New(&buf, "info", "testing") - - logger.Infof("Hello %s!", name) - logger.Info("Hello ", name) - logger.Debug("Hello Log!") - - fmt.Print(&buf) -} diff --git a/p2p/connection.go b/p2p/connection.go index 6f3eac24d6..79d2c6cf5c 100644 --- a/p2p/connection.go +++ b/p2p/connection.go @@ -13,6 +13,7 @@ import ( "time" "github.com/FactomProject/factomd/common/primitives" + "github.com/FactomProject/factomd/log" ) // Connection represents a single connection to another peer over the network. It communicates with the application @@ -41,6 +42,7 @@ type Connection struct { isPersistent bool // Persistent connections we always redail. notes string // Notes about the connection, for debugging (eg: error) metrics ConnectionMetrics // Metrics about this connection + Logger *log.FLogger } // Each connection is a simple state machine. The state is managed by a single goroutine which also does netowrking. @@ -509,8 +511,10 @@ func (c *Connection) handleNetErrors(toss bool) { default: // Only go offline once per handleNetErrors call if !toss && !done { + c.Logger.Errorf("%s : HandleNetErros Going Offline due to -- %s", c.peer.PeerIdent(), err.Error()) c.goOffline() } + done = true } default: diff --git a/p2p/controller.go b/p2p/controller.go index 8f21ea669a..6d543f7d20 100644 --- a/p2p/controller.go +++ b/p2p/controller.go @@ -19,6 +19,7 @@ import ( "unicode" "github.com/FactomProject/factomd/common/primitives" + "github.com/FactomProject/factomd/log" ) // Controller manages the peer to peer network. @@ -52,6 +53,9 @@ type Controller struct { lastPeerRequest time.Time // Last time we asked peers about the peers they know about. specialPeersString string // configuration set special peers partsAssembler *PartsAssembler // a data structure that assembles full messages from received message parts + + // Logger + Logger *log.FLogger } type ControllerInit struct { @@ -62,6 +66,8 @@ type ControllerInit struct { SeedURL string // URL to a source of peer info SpecialPeers string // Peers to always connect to at startup, and stay persistent ConnectionMetricsChannel chan interface{} // Channel on which we put the connection metrics map, periodically. + LogPath string // Path for logs + LogLevel string // Logging level } // CommandDialPeer is used to instruct the Controller to dial a peer address @@ -188,6 +194,7 @@ func (c *Controller) Init(ci ControllerInit) *Controller { c.partsAssembler = new(PartsAssembler).Init() discovery := new(Discovery).Init(ci.PeersFile, ci.SeedURL) c.discovery = *discovery + c.Logger = log.NewLogFromConfig(ci.LogPath, ci.LogLevel, "Networking") // Set this to the past so we will do peer management almost right away after starting up. note("ctrlr", "\n\n\n\n\nController.Init(%s) Controller is: %+v\n\n", ci.Port, c) return c @@ -542,6 +549,7 @@ func (c *Controller) handleCommand(command interface{}) { case CommandDialPeer: // parameter is the peer address parameters := command.(CommandDialPeer) conn := new(Connection).Init(parameters.peer, parameters.persistent) + conn.Logger = c.Logger conn.Start() c.connections[conn.peer.Hash] = conn @@ -555,6 +563,7 @@ func (c *Controller) handleCommand(command interface{}) { peer := new(Peer).Init(addPort[0], addPort[1], 0, RegularPeer, 0) peer.Source["Accept()"] = time.Now() connection := new(Connection).InitWithConn(conn, *peer) + connection.Logger = c.Logger connection.Start() c.connections[connection.peer.Hash] = connection diff --git a/p2p/protocol.go b/p2p/protocol.go index a678a7d3de..9514fbce60 100644 --- a/p2p/protocol.go +++ b/p2p/protocol.go @@ -146,29 +146,29 @@ func dot(dot string) { } func silence(component string, format string, v ...interface{}) { - log(Silence, component, format, v...) + logP(Silence, component, format, v...) } func significant(component string, format string, v ...interface{}) { - log(Significant, component, format, v...) + logP(Significant, component, format, v...) } func logfatal(component string, format string, v ...interface{}) { - log(Fatal, component, format, v...) + logP(Fatal, component, format, v...) } func logerror(component string, format string, v ...interface{}) { - log(Errors, component, format, v...) + logP(Errors, component, format, v...) } func note(component string, format string, v ...interface{}) { - log(Notes, component, format, v...) + logP(Notes, component, format, v...) } func debug(component string, format string, v ...interface{}) { - log(Debugging, component, format, v...) + logP(Debugging, component, format, v...) } func verbose(component string, format string, v ...interface{}) { - log(Verbose, component, format, v...) + logP(Verbose, component, format, v...) } -// log is the base log function to produce parsable log output for mass metrics consumption -func log(level uint8, component string, format string, v ...interface{}) { +// logP is the base log function to produce parsable log output for mass metrics consumption +func logP(level uint8, component string, format string, v ...interface{}) { message := strings.Replace(fmt.Sprintf(format, v...), ",", "-", -1) // Make CSV parsable. // levelStr := LoggingLevels[level] // host, _ := os.Hostname() diff --git a/state/identity.go b/state/identity.go index 11b633cbcf..a71639d41b 100644 --- a/state/identity.go +++ b/state/identity.go @@ -15,7 +15,6 @@ import ( "github.com/FactomProject/factomd/common/interfaces" "github.com/FactomProject/factomd/common/messages" "github.com/FactomProject/factomd/common/primitives" - "github.com/FactomProject/factomd/log" ) var ( @@ -229,7 +228,7 @@ func (st *State) isIdentityChain(cid interfaces.IHash) int { // Eg. Only call from addserver or you don't want any messages being sent. func LoadIdentityByEntryBlock(eblk interfaces.IEntryBlock, st *State) { if eblk == nil { - log.Println("DEBUG: Identity Error, EBlock nil, disregard") + st.Logger.Infof("Initializing identity failed as eblock is nil") return } cid := eblk.GetChainID() @@ -267,21 +266,33 @@ func LoadIdentityByEntry(ent interfaces.IEBEntry, st *State, height uint32, init registerIdentityAsServer(ent, height, st) } else if string(ent.ExternalIDs()[1]) == "New Block Signing Key" { if len(ent.ExternalIDs()) == 7 { - RegisterBlockSigningKey(ent, initial, height, st) + err := RegisterBlockSigningKey(ent, initial, height, st) + if err != nil { + st.Logger.Warning("Identity Error: Updating Matryoshka Hash failed on AppendExtIDs() - %s", err.Error()) + } } } else if string(ent.ExternalIDs()[1]) == "New Bitcoin Key" { if len(ent.ExternalIDs()) == 9 { - RegisterAnchorSigningKey(ent, initial, height, st, "BTC") + err := RegisterAnchorSigningKey(ent, initial, height, st, "BTC") + if err != nil { + st.Logger.Warning("Identity Error: Updating Matryoshka Hash failed on AppendExtIDs() - %s", err.Error()) + } } } else if string(ent.ExternalIDs()[1]) == "New Matryoshka Hash" { if len(ent.ExternalIDs()) == 7 { - UpdateMatryoshkaHash(ent, initial, height, st) + err := UpdateMatryoshkaHash(ent, initial, height, st) + if err != nil { + st.Logger.Warning("Identity Error: Updating Matryoshka Hash failed on AppendExtIDs() - %s", err.Error()) + } } } else if len(ent.ExternalIDs()) > 1 && string(ent.ExternalIDs()[1]) == "Identity Chain" { addIdentity(ent, height, st) } else if len(ent.ExternalIDs()) > 1 && string(ent.ExternalIDs()[1]) == "Server Management" { if len(ent.ExternalIDs()) == 4 { - UpdateManagementKey(ent, height, st) + err := UpdateManagementKey(ent, height, st) + if err != nil { + st.Logger.Warning("Identity Error: Updating Matryoshka Hash failed on AppendExtIDs() - %s", err.Error()) + } } } } @@ -582,7 +593,7 @@ func UpdateMatryoshkaHash(entry interfaces.IEBEntry, initial bool, height uint32 sigmsg, err := AppendExtIDs(extIDs, 0, 4) if err != nil { //log.Printfln("Identity Error:", err) - return nil + return err } else { // Verify Signature idKey := st.Identities[IdentityIndex].Key1 @@ -732,7 +743,7 @@ func ProcessIdentityToAdminBlock(st *State, chainID interfaces.IHash, servertype err := st.AddIdentityFromChainID(chainID) if err != nil { - log.Println(err.Error()) + st.Logger.Warningf("Identity Failed to process AddServerMessage for %s : %s", chainID.String()[:10], err.Error()) return true } @@ -743,7 +754,7 @@ func ProcessIdentityToAdminBlock(st *State, chainID interfaces.IHash, servertype zero := primitives.NewZeroHash() if id.SigningKey == nil || id.SigningKey.IsSameAs(zero) { - log.Println("New Fed/Audit server [" + chainID.String()[:10] + "] does not have an Block Signing Key associated to it") + st.Logger.Warningf("Identity Failed to process AddServerMessage for %s", "New Fed/Audit server ["+chainID.String()[:10]+"] does not have an Block Signing Key associated to it") if !statusIsFedOrAudit(id.Status) { st.removeIdentity(index) } @@ -753,7 +764,7 @@ func ProcessIdentityToAdminBlock(st *State, chainID interfaces.IHash, servertype } if id.AnchorKeys == nil { - log.Println("New Fed/Audit server [" + chainID.String()[:10] + "] does not have an BTC Anchor Key associated to it") + st.Logger.Warningf("Identity Failed to process AddServerMessage for %s", "New Fed/Audit server ["+chainID.String()[:10]+"] does not have an BTC Anchor Key associated to it") if !statusIsFedOrAudit(id.Status) { st.removeIdentity(index) } @@ -767,7 +778,7 @@ func ProcessIdentityToAdminBlock(st *State, chainID interfaces.IHash, servertype } if id.MatryoshkaHash == nil || id.MatryoshkaHash.IsSameAs(zero) { - log.Println("New Fed/Audit server [" + chainID.String()[:10] + "] does not have an Matryoshka Hash associated to it") + st.Logger.Warningf("Identity Failed to process AddServerMessage for %s", "New Fed/Audit server ["+chainID.String()[:10]+"] does not have an Matryoshka Hash associated to it") if !statusIsFedOrAudit(id.Status) { st.removeIdentity(index) } @@ -782,7 +793,7 @@ func ProcessIdentityToAdminBlock(st *State, chainID interfaces.IHash, servertype } st.Identities[index] = id } else { - log.Println("New Fed/Audit server [" + chainID.String()[:10] + "] does not have an identity associated to it") + st.Logger.Warningf("Identity Failed to process AddServerMessage for %s", "New Fed/Audit server ["+chainID.String()[:10]+"] does not have an identity associated to it") return true } diff --git a/state/inMsgQueue.go b/state/inMsgQueue.go index 52f2aa37b2..2d73880a14 100644 --- a/state/inMsgQueue.go +++ b/state/inMsgQueue.go @@ -4,11 +4,29 @@ import ( "github.com/FactomProject/factomd/common/interfaces" ) +var inMsgQueueRateKeeper *RateCalculator + +// InMsgQueueRatePrometheus is for setting the appropriate prometheus calls +type InMsgQueueRatePrometheus struct{} + +func (InMsgQueueRatePrometheus) SetArrivalInstantAvg(v float64) { InMsgInstantArrivalQueueRate.Set(v) } +func (InMsgQueueRatePrometheus) SetArrivalTotalAvg(v float64) { InMsgTotalArrivalQueueRate.Set(v) } +func (InMsgQueueRatePrometheus) SetArrivalBackup(v float64) { InMsgQueueBackupRate.Set(v) } +func (InMsgQueueRatePrometheus) SetCompleteInstantAvg(v float64) { + InMsgInstantCompleteQueueRate.Set(v) +} +func (InMsgQueueRatePrometheus) SetCompleteTotalAvg(v float64) { InMsgTotalCompleteQueueRate.Set(v) } +func (InMsgQueueRatePrometheus) SetMovingArrival(v float64) { InMsgMovingArrivalQueueRate.Set(v) } +func (InMsgQueueRatePrometheus) SetMovingComplete(v float64) { InMsgMovingCompleteQueueRate.Set(v) } + // InMsgMSGQueue counts incoming and outgoing messages for inmsg queue type InMsgMSGQueue chan interfaces.IMsg func NewInMsgQueue(capacity int) InMsgMSGQueue { channel := make(chan interfaces.IMsg, capacity) + rc := NewRateCalculator(new(InMsgQueueRatePrometheus)) + go rc.Start() + inMsgQueueRateKeeper = rc return channel } @@ -24,6 +42,7 @@ func (q InMsgMSGQueue) Cap() int { // Enqueue adds item to channel and instruments based on type func (q InMsgMSGQueue) Enqueue(m interfaces.IMsg) { + inMsgQueueRateKeeper.Arrival() measureMessage(q, m, true) q <- m } @@ -34,6 +53,7 @@ func (q InMsgMSGQueue) Dequeue() interfaces.IMsg { select { case v := <-q: measureMessage(q, v, false) + inMsgQueueRateKeeper.Complete() return v default: return nil @@ -44,6 +64,7 @@ func (q InMsgMSGQueue) Dequeue() interfaces.IMsg { func (q InMsgMSGQueue) BlockingDequeue() interfaces.IMsg { v := <-q measureMessage(q, v, false) + inMsgQueueRateKeeper.Complete() return v } @@ -149,13 +170,13 @@ func (q InMsgMSGQueue) Heartbeat(increment bool) { TotalMessageQueueInMsgHeartbeat.Inc() } -func (q InMsgMSGQueue) InvalidDBlock(increment bool) { +func (q InMsgMSGQueue) EtcdHashPickup(increment bool) { if !increment { - CurrentMessageQueueInMsgInvalidDB.Dec() + CurrentMessageQueueInMsgEtcdHashPickup.Dec() return } - CurrentMessageQueueInMsgInvalidDB.Inc() - TotalMessageQueueInMsgInvalidDB.Inc() + CurrentMessageQueueInMsgEtcdHashPickup.Inc() + TotalMessageQueueInMsgEtcdHashPickup.Inc() } func (q InMsgMSGQueue) MissingMsg(increment bool) { diff --git a/state/instrumentation.go b/state/instrumentation.go index 2cab5999cc..e3381c101f 100644 --- a/state/instrumentation.go +++ b/state/instrumentation.go @@ -123,8 +123,8 @@ var ( Name: "factomd_state_queue_current_inmsg_heatbeat", Help: "Instrumenting the inmsg queue", }) - CurrentMessageQueueInMsgInvalidDB = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "factomd_state_queue_current_inmsg_invaliddb", + CurrentMessageQueueInMsgEtcdHashPickup = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_current_inmsg_etcdpickup", Help: "Instrumenting the inmsg queue", }) CurrentMessageQueueInMsgMissingMsg = prometheus.NewGauge(prometheus.GaugeOpts{ @@ -216,8 +216,8 @@ var ( Name: "factomd_state_queue_total_inmsg_heatbeat", Help: "Instrumenting the inmsg queue", }) - TotalMessageQueueInMsgInvalidDB = prometheus.NewCounter(prometheus.CounterOpts{ - Name: "factomd_state_queue_total_inmsg_invaliddb", + TotalMessageQueueInMsgEtcdHashPickup = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "factomd_state_queue_total_inmsg_etcdpickup", Help: "Instrumenting the inmsg queue", }) TotalMessageQueueInMsgMissingMsg = prometheus.NewCounter(prometheus.CounterOpts{ @@ -310,8 +310,8 @@ var ( Name: "factomd_state_queue_total_netoutmsg_heatbeat", Help: "Instrumenting the netoutmsg queue", }) - TotalMessageQueueNetOutMsgInvalidDB = prometheus.NewCounter(prometheus.CounterOpts{ - Name: "factomd_state_queue_total_netoutmsg_invaliddb", + TotalMessageQueueNetOutMsgEtcdHashPickup = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "factomd_state_queue_total_netoutmsg_etcdpickup", Help: "Instrumenting the netoutmsg queue", }) TotalMessageQueueNetOutMsgMissingMsg = prometheus.NewCounter(prometheus.CounterOpts{ @@ -358,6 +358,78 @@ var ( Name: "factomd_state_queue_total_netoutmsg_misc", Help: "Instrumenting the netoutmsg queue", }) + + // InMsgQueue Rates + InMsgTotalArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_total_inmsg", + Help: "Total avg of inmsg queue arrival rate", + }) + + InMsgInstantArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_instant_inmsg", + Help: "Instant avg of inmsg queue arrival rate", + }) + + InMsgMovingArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_moving_inmsg", + Help: "Moving avg of inmsg queue arrival rate", + }) + + InMsgTotalCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_total_inmsg", + Help: "Total avg of inmsg queue complete rate", + }) + + InMsgInstantCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_instant_inmsg", + Help: "Instant avg of inmsg queue complete rate", + }) + + InMsgMovingCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_moving_inmsg", + Help: "Moving avg of inmsg queue complete rate", + }) + + InMsgQueueBackupRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_backup_inmsg", + Help: "Backup of queue", + }) + + // NetOut Rates + NetOutTotalArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_total_netout", + Help: "Total avg of inmsg queue arrival rate", + }) + + NetOutInstantArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_instant_netout", + Help: "Instant avg of inmsg queue arrival rate", + }) + + NetOutMovingArrivalQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_arrival_avg_moving_netout", + Help: "Moving avg of inmsg queue arrival rate", + }) + + NetOutTotalCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_total_netout", + Help: "Total avg of inmsg queue complete rate", + }) + + NetOutInstantCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_instant_netout", + Help: "Instant avg of inmsg queue complete rate", + }) + + NetOutMovingCompleteQueueRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_complete_avg_moving_netout", + Help: "Moving avg of inmsg queue complete rate", + }) + + NetOutQueueBackupRate = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "factomd_state_queue_backup_netout", + Help: "Backup of queue", + }) ) var registered bool = false @@ -404,7 +476,7 @@ func RegisterPrometheus() { prometheus.MustRegister(CurrentMessageQueueInMsgEOMTimeout) prometheus.MustRegister(CurrentMessageQueueInMsgFactTX) prometheus.MustRegister(CurrentMessageQueueInMsgHeartbeat) - prometheus.MustRegister(CurrentMessageQueueInMsgInvalidDB) + prometheus.MustRegister(CurrentMessageQueueInMsgEtcdHashPickup) prometheus.MustRegister(CurrentMessageQueueInMsgMissingMsg) prometheus.MustRegister(CurrentMessageQueueInMsgMissingMsgResp) prometheus.MustRegister(CurrentMessageQueueInMsgMissingData) @@ -428,7 +500,7 @@ func RegisterPrometheus() { prometheus.MustRegister(TotalMessageQueueInMsgEOMTimeout) prometheus.MustRegister(TotalMessageQueueInMsgFactTX) prometheus.MustRegister(TotalMessageQueueInMsgHeartbeat) - prometheus.MustRegister(TotalMessageQueueInMsgInvalidDB) + prometheus.MustRegister(TotalMessageQueueInMsgEtcdHashPickup) prometheus.MustRegister(TotalMessageQueueInMsgMissingMsg) prometheus.MustRegister(TotalMessageQueueInMsgMissingMsgResp) prometheus.MustRegister(TotalMessageQueueInMsgMissingData) @@ -453,7 +525,7 @@ func RegisterPrometheus() { prometheus.MustRegister(TotalMessageQueueNetOutMsgEOMTimeout) prometheus.MustRegister(TotalMessageQueueNetOutMsgFactTX) prometheus.MustRegister(TotalMessageQueueNetOutMsgHeartbeat) - prometheus.MustRegister(TotalMessageQueueNetOutMsgInvalidDB) + prometheus.MustRegister(TotalMessageQueueNetOutMsgEtcdHashPickup) prometheus.MustRegister(TotalMessageQueueNetOutMsgMissingMsg) prometheus.MustRegister(TotalMessageQueueNetOutMsgMissingMsgResp) prometheus.MustRegister(TotalMessageQueueNetOutMsgMissingData) @@ -465,4 +537,22 @@ func RegisterPrometheus() { prometheus.MustRegister(TotalMessageQueueNetOutMsgBounceMsg) prometheus.MustRegister(TotalMessageQueueNetOutMsgBounceResp) prometheus.MustRegister(TotalMessageQueueNetOutMsgMisc) + + // InMsgRate + prometheus.MustRegister(InMsgTotalArrivalQueueRate) + prometheus.MustRegister(InMsgInstantArrivalQueueRate) + prometheus.MustRegister(InMsgTotalCompleteQueueRate) + prometheus.MustRegister(InMsgInstantCompleteQueueRate) + prometheus.MustRegister(InMsgQueueBackupRate) + prometheus.MustRegister(InMsgMovingArrivalQueueRate) + prometheus.MustRegister(InMsgMovingCompleteQueueRate) + + // NetOutRate + prometheus.MustRegister(NetOutTotalArrivalQueueRate) + prometheus.MustRegister(NetOutInstantArrivalQueueRate) + prometheus.MustRegister(NetOutTotalCompleteQueueRate) + prometheus.MustRegister(NetOutInstantCompleteQueueRate) + prometheus.MustRegister(NetOutQueueBackupRate) + prometheus.MustRegister(NetOutMovingArrivalQueueRate) + prometheus.MustRegister(NetOutMovingCompleteQueueRate) } diff --git a/state/loadDatabase.go b/state/loadDatabase.go index 3b2b113b0f..6e8d1c7050 100644 --- a/state/loadDatabase.go +++ b/state/loadDatabase.go @@ -36,7 +36,7 @@ func LoadDatabase(s *State) { blkCnt = head.GetHeader().GetDBHeight() } - t := time.Now() + last := time.Now() //msg, err := s.LoadDBState(blkCnt) start := s.GetDBHeightComplete() @@ -46,10 +46,9 @@ func LoadDatabase(s *State) { for i := int(start); i <= int(blkCnt); i++ { if i > 0 && i%1000 == 0 { - since := time.Since(t) - ss := float64(since.Nanoseconds()) / 1000000000 - bps := float64(i) / ss + bps := float64(1000) / time.Since(last).Seconds() os.Stderr.WriteString(fmt.Sprintf("%20s Loading Block %7d / %v. Blocks per second %8.2f\n", s.FactomNodeName, i, blkCnt, bps)) + last = time.Now() } msg, err := s.LoadDBState(uint32(i)) diff --git a/state/netOutMsgQueue.go b/state/netOutMsgQueue.go index ca2a28e216..d12d2ed38c 100644 --- a/state/netOutMsgQueue.go +++ b/state/netOutMsgQueue.go @@ -4,11 +4,32 @@ import ( "github.com/FactomProject/factomd/common/interfaces" ) -// NetOutMsgQueue counts incoming and outgoing messages for inmsg queue +var NetOutMsgQueueRateKeeper *RateCalculator + +// NetOutQueueRatePrometheus is for setting the appropriate prometheus calls +type NetOutQueueRatePrometheus struct{} + +func (NetOutQueueRatePrometheus) SetArrivalInstantAvg(v float64) { + NetOutInstantArrivalQueueRate.Set(v) +} +func (NetOutQueueRatePrometheus) SetArrivalTotalAvg(v float64) { NetOutTotalArrivalQueueRate.Set(v) } +func (NetOutQueueRatePrometheus) SetArrivalBackup(v float64) { NetOutQueueBackupRate.Set(v) } +func (NetOutQueueRatePrometheus) SetCompleteInstantAvg(v float64) { + NetOutInstantCompleteQueueRate.Set(v) +} +func (NetOutQueueRatePrometheus) SetCompleteTotalAvg(v float64) { NetOutTotalCompleteQueueRate.Set(v) } +func (NetOutQueueRatePrometheus) SetMovingArrival(v float64) { NetOutMovingArrivalQueueRate.Set(v) } +func (NetOutQueueRatePrometheus) SetMovingComplete(v float64) { NetOutMovingCompleteQueueRate.Set(v) } + +// NetOutMsgQueue counts incoming and outgoing messages for netout queue type NetOutMsgQueue chan interfaces.IMsg func NewNetOutMsgQueue(capacity int) NetOutMsgQueue { channel := make(chan interfaces.IMsg, capacity) + rc := NewRateCalculator(new(NetOutQueueRatePrometheus)) + go rc.Start() + NetOutMsgQueueRateKeeper = rc + return channel } @@ -24,6 +45,7 @@ func (q NetOutMsgQueue) Cap() int { // Enqueue adds item to channel and instruments based on type func (q NetOutMsgQueue) Enqueue(m interfaces.IMsg) { + NetOutMsgQueueRateKeeper.Arrival() measureMessage(q, m, true) q <- m } @@ -33,6 +55,7 @@ func (q NetOutMsgQueue) Enqueue(m interfaces.IMsg) { func (q NetOutMsgQueue) Dequeue() interfaces.IMsg { select { case v := <-q: + NetOutMsgQueueRateKeeper.Complete() return v default: return nil @@ -42,6 +65,7 @@ func (q NetOutMsgQueue) Dequeue() interfaces.IMsg { // BlockingDequeue will block until it retrieves from queue func (q NetOutMsgQueue) BlockingDequeue() interfaces.IMsg { v := <-q + NetOutMsgQueueRateKeeper.Complete() return v } @@ -92,8 +116,8 @@ func (q NetOutMsgQueue) Heartbeat(increment bool) { TotalMessageQueueNetOutMsgHeartbeat.Inc() } -func (q NetOutMsgQueue) InvalidDBlock(increment bool) { - TotalMessageQueueNetOutMsgInvalidDB.Inc() +func (q NetOutMsgQueue) EtcdHashPickup(increment bool) { + TotalMessageQueueNetOutMsgEtcdHashPickup.Inc() } func (q NetOutMsgQueue) MissingMsg(increment bool) { diff --git a/state/queues.go b/state/queues.go index edb39a83d8..7684760755 100644 --- a/state/queues.go +++ b/state/queues.go @@ -1,5 +1,20 @@ package state +// +// Addressing Performance +// IQueues replace channels and monitor enqueues and dequeues +// with prometheus instrumentation. By tripping a prometheus call, +// performance is lost, but compared to the insight gained, is worth it. +// The performance does not affect our queue management. +// +// Benchmarks :: `go test -bench=. queues_test.go ` +// BenchmarkChannels-4 20000000 94.7 ns/op +// BenchmarkQueues-4 10000000 153 ns/op +// BenchmarkConcurentChannels-4 10000000 138 ns/op +// BenchmarkConcurrentQueues-4 5000000 251 ns/op +// BenchmarkCompetingChannels-4 3000000 360 ns/op +// BenchmarkCompetingQueues-4 1000000 1302 ns/op + import ( "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/interfaces" @@ -53,7 +68,7 @@ type IPrometheusChannel interface { EOMTimeout(increment bool) FactTx(increment bool) Heartbeat(increment bool) - InvalidDBlock(increment bool) + EtcdHashPickup(increment bool) MissingMsg(increment bool) MissingMsgResp(increment bool) MissingData(increment bool) @@ -96,7 +111,7 @@ func measureMessage(channel IPrometheusChannel, msg interfaces.IMsg, increment b case constants.HEARTBEAT_MSG: // 11 channel.Heartbeat(increment) case constants.INVALID_DIRECTORY_BLOCK_MSG: // 12 - channel.InvalidDBlock(increment) + channel.EtcdHashPickup(increment) case constants.MISSING_MSG: // 13 channel.MissingMsg(increment) case constants.MISSING_MSG_RESPONSE: // 14 diff --git a/state/queues_test.go b/state/queues_test.go index 21353043e8..d9eab2e570 100644 --- a/state/queues_test.go +++ b/state/queues_test.go @@ -13,6 +13,7 @@ import ( var _ = fmt.Println func TestQueues(t *testing.T) { + var _, _ = NewInMsgQueue(0), NewNetOutMsgQueue(0) RegisterPrometheus() RegisterPrometheus() channel := make(chan interfaces.IMsg, 1000) @@ -161,3 +162,83 @@ func checkLensAndCap(channel chan interfaces.IMsg, qs []interfaces.IQueue) bool } return true } + +// Only 1 write/read thread + +func BenchmarkChannels(b *testing.B) { + c := make(chan interfaces.IMsg, 1000) + for i := 0; i < b.N; i++ { + c <- nil + <-c + } +} + +func BenchmarkQueues(b *testing.B) { + c := NewInMsgQueue(1000) + for i := 0; i < b.N; i++ { + c.Enqueue(nil) + c.Dequeue() + } +} + +// 2 threads write/read, but 1 thread is not aggressively adding + +func BenchmarkConcurentChannels(b *testing.B) { + c := make(chan interfaces.IMsg, 1000) + go func() { + for true { + c <- nil + <-c + time.Sleep(10 * time.Nanosecond) + } + }() + for i := 0; i < b.N; i++ { + c <- nil + <-c + } +} + +func BenchmarkConcurrentQueues(b *testing.B) { + c := NewInMsgQueue(1000) + go func() { + for true { + c.Enqueue(nil) + c.Dequeue() + time.Sleep(10 * time.Nanosecond) + } + }() + for i := 0; i < b.N; i++ { + c.Enqueue(nil) + c.Dequeue() + } +} + +// 2 threads aggressively reading/writing + +func BenchmarkCompetingChannels(b *testing.B) { + c := make(chan interfaces.IMsg, 1000) + go func() { + for true { + c <- nil + <-c + } + }() + for i := 0; i < b.N; i++ { + c <- nil + <-c + } +} + +func BenchmarkCompetingQueues(b *testing.B) { + c := NewInMsgQueue(1000) + go func() { + for true { + c.Enqueue(nil) + c.Dequeue() + } + }() + for i := 0; i < b.N; i++ { + c.Enqueue(nil) + c.Dequeue() + } +} diff --git a/state/rateCalculator.go b/state/rateCalculator.go new file mode 100644 index 0000000000..34b8f16145 --- /dev/null +++ b/state/rateCalculator.go @@ -0,0 +1,165 @@ +package state + +import ( + "sync/atomic" + "time" +) + +// IPrometheusRateMethods indicated which prometheus counters/gauges to set +type IPrometheusRateMethods interface { + // Arrival + SetArrivalInstantAvg(v float64) + SetArrivalTotalAvg(v float64) + SetArrivalBackup(v float64) + SetMovingArrival(v float64) + + // Complete + SetCompleteInstantAvg(v float64) + SetCompleteTotalAvg(v float64) + SetMovingComplete(v float64) +} + +// RateCalculator will maintain the rate of msgs arriving and rate of msgs +// leaving a queue. The instant rate is a 2s avg +type RateCalculator struct { + // Accessed on potentially Multiple Threads + prometheusMethods IPrometheusRateMethods + arrival *int32 + completed *int32 + line *int32 + + // Single threaded + tickerTime time.Duration + rollingArrival *MovingAverage + rollingComplete *MovingAverage +} + +// NewRateCalculatorTime is good for unit tests, or if you want to change the measureing time +func NewRateCalculatorTime(p IPrometheusRateMethods, td time.Duration) *RateCalculator { + r := new(RateCalculator) + r.prometheusMethods = p + + r.arrival = new(int32) + r.completed = new(int32) + r.line = new(int32) + r.tickerTime = td + + r.rollingArrival = NewMovingAverage(10) + r.rollingComplete = NewMovingAverage(10) + + return r +} + +func NewRateCalculator(p IPrometheusRateMethods) *RateCalculator { + return NewRateCalculatorTime(p, time.Duration(2*time.Second)) +} + +// Start begins instrumentation +func (r *RateCalculator) Start() { + r.StartTime(time.Now()) +} + +// StartTime is good for unit tests +func (r *RateCalculator) StartTime(start time.Time) { + var totalArrival int32 = 0 + var totalComplete int32 = 0 + + ticker := time.NewTicker(r.tickerTime) + // Every 2 seconds caluclate the instant rate and adjust the total avg + for _ = range ticker.C { + na, nc := int32(0), int32(0) + + // + // Grab the current values and reset + ca := atomic.SwapInt32(r.arrival, na) + cc := atomic.SwapInt32(r.completed, nc) + cl := atomic.LoadInt32(r.line) + + totalArrival += ca + totalComplete += cc + + r.rollingArrival.Add(float64(ca)) + r.rollingComplete.Add(float64(cc)) + + // Calculate Total Avg + totalTime := time.Since(start).Seconds() + r.prometheusMethods.SetArrivalTotalAvg(float64(totalArrival) / totalTime) + r.prometheusMethods.SetCompleteTotalAvg(float64(totalComplete) / totalTime) + + // Calculate 2s Avg + r.prometheusMethods.SetArrivalInstantAvg(float64(ca) / r.tickerTime.Seconds()) + r.prometheusMethods.SetCompleteInstantAvg(float64(cc) / r.tickerTime.Seconds()) + + // Moving Avg + r.prometheusMethods.SetMovingArrival(r.rollingArrival.Avg() / r.tickerTime.Seconds()) + r.prometheusMethods.SetMovingComplete(r.rollingComplete.Avg() / r.tickerTime.Seconds()) + + // Set the backup + r.prometheusMethods.SetArrivalBackup(float64(cl)) + } +} + +// Arrival indicates a new item added to the queue +func (r *RateCalculator) Arrival() { + atomic.AddInt32(r.arrival, 1) + atomic.AddInt32(r.line, 1) +} + +// Complete indicates something left the queue +func (r *RateCalculator) Complete() { + atomic.AddInt32(r.completed, 1) + atomic.AddInt32(r.line, -1) +} + +type MovingAverage struct { + Window int + values []float64 + valPos int + slotsFilled bool +} + +func (ma *MovingAverage) Avg() float64 { + var sum = float64(0) + var c = ma.Window - 1 + + // Are all slots filled? If not, ignore unused + if !ma.slotsFilled { + c = ma.valPos - 1 + if c < 0 { + // Empty register + return 0 + } + } + + // Sum values + var ic = 0 + for i := 0; i <= c; i++ { + sum += ma.values[i] + ic++ + } + + // Finalize average and return + avg := sum / float64(ic) + return avg +} + +func (ma *MovingAverage) Add(val float64) { + // Put into values array + ma.values[ma.valPos] = val + + // Increment value position + ma.valPos = (ma.valPos + 1) % ma.Window + + if !ma.slotsFilled && ma.valPos == 0 { + ma.slotsFilled = true + } +} + +func NewMovingAverage(window int) *MovingAverage { + return &MovingAverage{ + Window: window, + values: make([]float64, window), + valPos: 0, + slotsFilled: false, + } +} diff --git a/state/rateCalculator_test.go b/state/rateCalculator_test.go new file mode 100644 index 0000000000..912e0762dc --- /dev/null +++ b/state/rateCalculator_test.go @@ -0,0 +1,246 @@ +package state_test + +import ( + "fmt" + "testing" + "time" + + "github.com/FactomProject/factomd/common/primitives/random" + . "github.com/FactomProject/factomd/state" +) + +type Exposer struct { + AWA float64 + ATA float64 + ABU float64 + + CWA float64 + CTA float64 + + CMA float64 + AMA float64 +} + +func NewExposer() *Exposer { + e := new(Exposer) + return e +} + +func (k *Exposer) SetArrivalInstantAvg(v float64) { + k.AWA = v +} + +func (k *Exposer) SetArrivalTotalAvg(v float64) { + k.ATA = v +} + +func (k *Exposer) SetArrivalBackup(v float64) { + k.ABU = v +} + +func (k *Exposer) SetCompleteInstantAvg(v float64) { + k.CWA = v +} + +func (k *Exposer) SetMovingArrival(v float64) { + k.AMA = v +} + +func (k *Exposer) SetMovingComplete(v float64) { + k.CMA = v +} + +func (k *Exposer) SetCompleteTotalAvg(v float64) { + k.CTA = v +} + +func (k *Exposer) String() string { + return fmt.Sprintf("%f, %f, %f, %f, %f", k.AWA, k.ATA, k.ABU, k.CWA, k.CTA) +} + +func close(a, b, tolerance float64) bool { + diff := a - b + if diff < 0 { + diff = diff * -1 + } + if diff < tolerance { + return true + } + return false +} + +func shouldbe(awa, abu, cwa float64, e *Exposer) error { + if !close(awa, e.AWA, 0.2) { + return fmt.Errorf("AWA is %f, should be %f", awa, e.AWA) + } + + // By Speeding up the tick time, these numbers are usually off + //if !close(ata, e.ATA, 15) { + //return fmt.Errorf("ATA is %f, should be %f", ata, e.ATA) + //} + + if !close(abu, e.ABU, 0.1) { + return fmt.Errorf("ABU is %f, should be %f", abu, e.ABU) + } + + if !close(cwa, e.CWA, 0.2) { + return fmt.Errorf("CWA is %f, should be %f", cwa, e.CWA) + } + + // By Speeding up the tick time, these numbers are usually off + //if !close(cta, e.CTA, 15) { + //return fmt.Errorf("CTA is %f, should be %f", cta, e.CTA) + //} + + return nil +} + +// Most of the time is sleeping, so run 10 in parallel +func TestRateCalculator(t *testing.T) { + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("ParallelTest%d", i), testRateCalculator) + } +} + +func testRateCalculator(t *testing.T) { + t.Parallel() + e := NewExposer() + td := time.Millisecond * 100 + rc := NewRateCalculatorTime(e, td) + + TotalA := float64(0) + TotalC := float64(0) + ac := func(add int) { + for i := 0; i < add; i++ { + rc.Arrival() + rc.Complete() + TotalC++ + TotalA++ + } + } + + fa := random.RandIntBetween(0, 100) + ac(fa) + start := time.Now() + go rc.StartTime(start) + ticker := time.NewTicker(td - 1*time.Millisecond) + i := 0 + + ataF := func(diff time.Duration) float64 { + return TotalA / (time.Since(start).Seconds() - diff.Seconds()) + } + + ctaF := func(diff time.Duration) float64 { + return TotalC / (time.Since(start).Seconds() - diff.Seconds()) + } + var _, _ = ataF, ctaF + +Outer: + for _ = range ticker.C { + switch i { + case 0: + if err := retry(float64(fa)/td.Seconds(), 0, float64(fa)/td.Seconds(), e, 20); err != nil { + t.Error("1", err) + } + case 1: + if err := retry(0, 0, 0, e, 20); err != nil { + t.Error("2", err) + } + go ac(fa * 3) + case 2: + if err := retry(float64(fa*3)/td.Seconds(), 0, float64(fa*3)/td.Seconds(), e, 20); err != nil { + t.Error("3", err) + } + case 3: + if err := retry(0, 0, 0, e, 20); err != nil { + t.Error("2", err) + } + break Outer + } + i++ + } +} + +func retry(awa, abu, cwa float64, e *Exposer, amt int) error { + var err error + for a := 0; a < amt; a++ { + err = shouldbe(awa, abu, cwa, e) + if err == nil { + return nil + } else { + if a >= amt { + return err + } + } + time.Sleep(1 * time.Millisecond) + } + return err +} + +func TestMovingAverage(t *testing.T) { + a := NewMovingAverage(5) + if a.Avg() != 0 { + t.Fail() + } + a.Add(2) + if a.Avg() < 1.999 || a.Avg() > 2.001 { + t.Fail() + } + a.Add(4) + a.Add(2) + if a.Avg() < 2.665 || a.Avg() > 2.667 { + t.Fail() + } + a.Add(4) + a.Add(2) + if a.Avg() < 2.799 || a.Avg() > 2.801 { + t.Fail() + } + + // This one will go into the first slot again + // evicting the first value + a.Add(10) + if a.Avg() < 4.399 || a.Avg() > 4.401 { + t.Fail() + } + + for i := 0; i < 10; i++ { + a.Add(0) + } + + if a.Avg() < 0-0.1 || a.Avg() > 0.+0.1 { + t.Fail() + } + + a = NewMovingAverage(5) + nums := make([]float64, 1000) + index := 0 + + for i := 0; i < 1000; i++ { + nums[i] = float64(random.RandIntBetween(0, 60000)) + } + + for ; index < 1000; index++ { + a.Add(nums[index]) + v := a.Avg() + tv := float64(0) + total := float64(0) + for sub := index; sub >= 0; sub-- { + if total >= 5 || sub < 0 { + break + } + total++ + tv += nums[sub] + } + + diff := v - (tv / total) + if diff < 0 { + diff = -1 * diff + } + + if diff > 0.1 { + t.Errorf("Difference is %f at index %d. Found %f, exp %f. Total %f", diff, index, v, tv/total, total) + t.Log(nums[:index+1]) + } + } +} diff --git a/state/saveAndRestore.go b/state/saveAndRestore.go index 94bb4f02b0..72304b341b 100644 --- a/state/saveAndRestore.go +++ b/state/saveAndRestore.go @@ -160,8 +160,23 @@ func (a *SaveState) IsSameAs(b *SaveState) bool { } } - //Identities []*Identity - //Authorities []*Authority + if len(a.Identities) != len(b.Identities) { + return false + } + for i := range a.Identities { + if a.Identities[i].IsSameAs(b.Identities[i]) == false { + fmt.Printf("%v: %v vs %v\n", i, a.Identities[i].String(), b.Identities[i].String()) + return false + } + } + if len(a.Authorities) != len(b.Authorities) { + return false + } + for i := range a.Authorities { + if a.Authorities[i].IsSameAs(b.Authorities[i]) == false { + return false + } + } if a.AuthorityServerCount != b.AuthorityServerCount { return false } diff --git a/state/state.go b/state/state.go index d9d41c034b..c1b8a4bd79 100644 --- a/state/state.go +++ b/state/state.go @@ -30,7 +30,6 @@ import ( "github.com/FactomProject/factomd/database/leveldb" "github.com/FactomProject/factomd/database/mapdb" "github.com/FactomProject/factomd/log" - "github.com/FactomProject/factomd/logger" "github.com/FactomProject/factomd/p2p" "github.com/FactomProject/factomd/util" "github.com/FactomProject/factomd/wsapi" @@ -245,7 +244,7 @@ type State struct { // Database DB interfaces.DBOverlaySimple - Logger *logger.FLogger + Logger *log.FLogger Anchor interfaces.IAnchor // Directory Block State @@ -360,10 +359,15 @@ func (s *State) Clone(cloneNumber int) interfaces.IState { config = true } + if s.LogPath == "stdout" { + newState.LogPath = "stdout" + } else { + newState.LogPath = s.LogPath + "/Sim" + number + } + newState.FactomNodeName = s.Prefix + "FNode" + number newState.FactomdVersion = s.FactomdVersion newState.DropRate = s.DropRate - newState.LogPath = s.LogPath + "/Sim" + number newState.LdbPath = s.LdbPath + "/Sim" + number newState.JournalFile = s.LogPath + "/journal" + number + ".log" newState.Journaling = s.Journaling @@ -716,9 +720,17 @@ func (s *State) Init() { s.RunLeader = false s.IgnoreMissing = true - wsapi.InitLogs(s.LogPath+s.FactomNodeName+".log", s.LogLevel) - - s.Logger = logger.NewLogFromConfig(s.LogPath, s.LogLevel, "State") + if s.LogPath == "stdout" { + wsapi.InitLogs(s.LogPath, s.LogLevel) + s.Logger = log.NewLogFromConfig(s.LogPath, s.LogLevel, "State") + } else { + er := os.MkdirAll(s.LogPath, 0777) + if er != nil { + // fmt.Println("Could not create " + s.LogPath + "\n error: " + er.Error()) + } + wsapi.InitLogs(s.LogPath+s.FactomNodeName+".log", s.LogLevel) + s.Logger = log.NewLogFromConfig(s.LogPath, s.LogLevel, "State") + } log.SetLevel(s.ConsoleLogLevel) @@ -738,10 +750,6 @@ func (s *State) Init() { s.UpdateEntryHash = make(chan *EntryUpdate, 10000) //Handles entry hashes and updating Commit maps. s.WriteEntry = make(chan interfaces.IEBEntry, 3000) //Entries to be written to the database - er := os.MkdirAll(s.LogPath, 0777) - if er != nil { - // fmt.Println("Could not create " + s.LogPath + "\n error: " + er.Error()) - } if s.Journaling { f, err := os.Create(s.JournalFile) if err != nil { @@ -2210,7 +2218,7 @@ func (s *State) SetStringQueues() { stps) if s.Balancehash == nil { - s.Balancehash = primitives.NewHash(constants.ZERO_HASH) + s.Balancehash = primitives.NewZeroHash() } str = str + fmt.Sprintf(" %d/%d", list.System.Height, len(list.System.List)) diff --git a/state/stateFER.go b/state/stateFER.go index 6b6e3531a4..3496675602 100644 --- a/state/stateFER.go +++ b/state/stateFER.go @@ -29,11 +29,13 @@ func (this *State) ProcessRecentFERChainEntries() { // Get the first eblock from the FERChain entryBlock, err := this.DB.FetchEBlockHead(FERChainHash) if err != nil { - this.Println("Couldn't find the FER chain for id ", this.FERChainId) + this.Logger.Infof("FER Chain head found to be nil", this.FERChainId) + // this.Println("Couldn't find the FER chain for id ", this.FERChainId) return } if entryBlock == nil { - this.Println("FER Chain head found to be nil") + this.Logger.Info("FER Chain head found to be nil") + // this.Println("FER Chain head found to be nil") return } diff --git a/state/stateSaver.go b/state/stateSaver.go index cb896dda2f..227bbf7aae 100644 --- a/state/stateSaver.go +++ b/state/stateSaver.go @@ -22,7 +22,7 @@ type StateSaverStruct struct { } //To be increased whenever the data being saved changes from the last verion -const version = 5 +const version = 6 func (sss *StateSaverStruct) StopSaving() { sss.Mutex.Lock() diff --git a/testHelper/ecBlock.go b/testHelper/ecBlock.go index b8fbe8c80e..086f48c79e 100644 --- a/testHelper/ecBlock.go +++ b/testHelper/ecBlock.go @@ -81,9 +81,13 @@ func NewCommitChain(eBlock *entryBlock.EBlock) *entryCreditBlock.CommitChain { panic(err) } commit.ChainIDHash = eBlock.GetHashOfChainIDHash() - commit.Weld = eBlock.GetWeldHashes()[0] - commit.EntryHash = eBlock.Body.EBEntries[0] + w := primitives.NewZeroHash() + eh0 := eBlock.GetEntryHashes()[0].Bytes() + cid := eBlock.GetHeader().GetChainID().Bytes() + w.SetBytes(primitives.DoubleSha(append(eh0, cid...))) + commit.Weld = w + commit.EntryHash = eBlock.Body.EBEntries[0] bin, err := commit.MarshalBinary() if err != nil { panic(err) diff --git a/testHelper/testHelper_test.go b/testHelper/testHelper_test.go index 3cc94e7ff4..d2d683d972 100644 --- a/testHelper/testHelper_test.go +++ b/testHelper/testHelper_test.go @@ -4,6 +4,9 @@ import ( "crypto/rand" "github.com/FactomProject/ed25519" //"github.com/FactomProject/factomd/common/factoid/wallet" + "encoding/hex" + "fmt" + "github.com/FactomProject/factomd/common/entryBlock" "github.com/FactomProject/factomd/common/primitives" . "github.com/FactomProject/factomd/testHelper" "testing" @@ -110,3 +113,18 @@ func TestAnchor(t *testing.T) { anchor := CreateFirstAnchorEntry() t.Errorf("%x", anchor.ChainID.Bytes()) }*/ + +func TestNewCommitChain(t *testing.T) { + j := new(entryBlock.EBlock) + //block 1000 + eblock1kbytes, _ := hex.DecodeString("df3ade9eec4b08d5379cc64270c30ea7315d8a8a1a69efe2b98a60ecdd69e6041611c693d62887530c5420a48f2ea2d6038745fc493d6b1e531232805dd2149614ef537df0c73df748b12d508b4334fe8d2832a4cd6ea24f64a3363839bd0efa46e835bfed10ded0d756d7ccafd44830cc942799fca43f2505e9d024b0a9dd3c00000221000003e800000002b24d4ee9e2184673a4d7de6fdac1288ea00b7856940341122c34bd50a662340a0000000000000000000000000000000000000000000000000000000000000009") + j.UnmarshalBinary(eblock1kbytes) + k := NewCommitChain(j) + m, _ := k.MarshalBinary() + //fmt.Printf("%x\n",m) + anticipated_commit := "010000000000e8aaec8504394192fc7f6129a024ec5919d38a3967955aa7bbb3ac0ff0879266937015fcc30d961f985ab8a8b5132273fa3fc72a246a68bce1d95cc8c6fdeb183cb24d4ee9e2184673a4d7de6fdac1288ea00b7856940341122c34bd50a662340a013b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29eb46be4cfa8f1e056565aae7d600ac1be4f2cc143333cbbff784b9efb1e8c86a79c333c1f748a45cee507cfabc5de59f6a41b7e83a0af0821de564dc99836b0b" + cf := fmt.Sprintf("%x", m) + if anticipated_commit != cf { + t.Errorf("testhelper NewCommitChain comparison failed") + } +} diff --git a/wsapi/ack.go b/wsapi/ack.go index 8a432340d1..5bf406b674 100644 --- a/wsapi/ack.go +++ b/wsapi/ack.go @@ -7,6 +7,7 @@ package wsapi import ( "encoding/hex" "fmt" + "time" "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/entryBlock" @@ -18,6 +19,9 @@ import ( ) func HandleV2FactoidACK(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallFctAck.Observe(float64(time.Since(n).Nanoseconds())) + ackReq := new(AckRequest) err := MapToObject(params, ackReq) if err != nil { @@ -95,6 +99,9 @@ func HandleV2FactoidACK(state interfaces.IState, params interface{}) (interface{ } func HandleV2EntryACK(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallEntryAck.Observe(float64(time.Since(n).Nanoseconds())) + ackReq := new(AckRequest) err := MapToObject(params, ackReq) diff --git a/wsapi/instrumentation.go b/wsapi/instrumentation.go new file mode 100644 index 0000000000..a8780bc00d --- /dev/null +++ b/wsapi/instrumentation.go @@ -0,0 +1,199 @@ +package wsapi + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +var ( + HandleV2APICallGeneral = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_general_call_ns", + Help: "Time it takes to compelete a call", + }) + + HandleV2APICallChainHead = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_chainhead_ns", + Help: "Time it takes to compelete a chainhead", + }) + + HandleV2APICallCommitChain = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_commitchain_ns", + Help: "Time it takes to compelete a commithcain", + }) + + HandleV2APICallCommitEntry = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_commitentry_ns", + Help: "Time it takes to compelete a commitentry", + }) + + HandleV2APICallDBlock = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_dblock_ns", + Help: "Time it takes to compelete a dblock", + }) + + HandleV2APICallDBlockHead = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_dblockhead_ns", + Help: "Time it takes to compelete a dblockhead", + }) + + HandleV2APICallEblock = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_eblock_ns", + Help: "Time it takes to compelete a eblock", + }) + + HandleV2APICallEntry = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_entry_ns", + Help: "Time it takes to compelete an entry", + }) + + HandleV2APICallECBal = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_ecbal_ns", + Help: "Time it takes to compelete a ecbal", + }) + + HandleV2APICallECRate = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_ecrate_ns", + Help: "Time it takes to compelete a ecrate", + }) + + HandleV2APICallFABal = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_fabal_ns", + Help: "Time it takes to compelete a fabal", + }) + + HandleV2APICallFctTx = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_fcttx_ns", + Help: "Time it takes to compelete a fcttx", + }) + + HandleV2APICallHeights = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_heights_ns", + Help: "Time it takes to compelete a heights", + }) + + HandleV2APICallProp = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_prop_ns", + Help: "Time it takes to compelete a prop", + }) + + HandleV2APICallRawData = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_rawdata_ns", + Help: "Time it takes to compelete a rawdata", + }) + + HandleV2APICallReceipt = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_receipt_ns", + Help: "Time it takes to compelete a ", + }) + + HandleV2APICallRevealEntry = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_reventry_ns", + Help: "Time it takes to compelete a revealentry", + }) + + HandleV2APICallFctAck = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_fctack_ns", + Help: "Time it takes to compelete a fctack", + }) + + HandleV2APICallEntryAck = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_entryack_ns", + Help: "Time it takes to compelete a entryack", + }) + + HandleV2APICall = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call__ns", + Help: "Time it takes to compelete a ", + }) + + HandleV2APICallPendingEntries = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_pendingentries_ns", + Help: "Time it takes to compelete a pendingentries", + }) + + HandleV2APICallPendingTxs = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_pendingtxs_ns", + Help: "Time it takes to compelete a pendingtxs", + }) + + HandleV2APICallSendRaw = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_sendraw_ns", + Help: "Time it takes to compelete a sendraw", + }) + + HandleV2APICallTransaction = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_tx_ns", + Help: "Time it takes to compelete a tx", + }) + + HandleV2APICallDBlockByHeight = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_dblockbyheight_ns", + Help: "Time it takes to compelete a dblockbyheight", + }) + + HandleV2APICallECBlockByHeight = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_ecblockbyheight_ns", + Help: "Time it takes to compelete a ecblockbyheight", + }) + + HandleV2APICallFblockByHeight = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_fblockbyheight_ns", + Help: "Time it takes to compelete a fblockbyheight", + }) + + HandleV2APICallABlockByHeight = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_ablockbyheight_ns", + Help: "Time it takes to compelete a ablockbyheight", + }) + + HandleV2APICallAuthorities = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_auths_ns", + Help: "Time it takes to compelete an auths ", + }) + + HandleV2APICallTpsRate = prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "factomd_wsapi_v2_api_call_tpsrate_ns", + Help: "Time it takes to compelete a tpsrate", + }) +) + +var registered = false + +// RegisterPrometheus registers the variables to be exposed. This can only be run once, hence the +// boolean flag to prevent panics if launched more than once. This is called in NetStart +func RegisterPrometheus() { + if registered { + return + } + registered = true + + prometheus.MustRegister(HandleV2APICallGeneral) + prometheus.MustRegister(HandleV2APICallChainHead) + prometheus.MustRegister(HandleV2APICallCommitChain) + prometheus.MustRegister(HandleV2APICallCommitEntry) + prometheus.MustRegister(HandleV2APICallDBlock) + prometheus.MustRegister(HandleV2APICallDBlockHead) + prometheus.MustRegister(HandleV2APICallEblock) + prometheus.MustRegister(HandleV2APICallEntry) + prometheus.MustRegister(HandleV2APICallECBal) + prometheus.MustRegister(HandleV2APICallECRate) + prometheus.MustRegister(HandleV2APICallFABal) + prometheus.MustRegister(HandleV2APICallFctTx) + prometheus.MustRegister(HandleV2APICallHeights) + prometheus.MustRegister(HandleV2APICallProp) + prometheus.MustRegister(HandleV2APICallRawData) + prometheus.MustRegister(HandleV2APICallReceipt) + prometheus.MustRegister(HandleV2APICallRevealEntry) + prometheus.MustRegister(HandleV2APICallFctAck) + prometheus.MustRegister(HandleV2APICallEntryAck) + prometheus.MustRegister(HandleV2APICall) + prometheus.MustRegister(HandleV2APICallPendingEntries) + prometheus.MustRegister(HandleV2APICallPendingTxs) + prometheus.MustRegister(HandleV2APICallSendRaw) + prometheus.MustRegister(HandleV2APICallTransaction) + prometheus.MustRegister(HandleV2APICallDBlockByHeight) + prometheus.MustRegister(HandleV2APICallECBlockByHeight) + prometheus.MustRegister(HandleV2APICallFblockByHeight) + prometheus.MustRegister(HandleV2APICallABlockByHeight) + prometheus.MustRegister(HandleV2APICallAuthorities) + prometheus.MustRegister(HandleV2APICallTpsRate) +} diff --git a/wsapi/log.go b/wsapi/log.go index f8f652be32..bec2185ca6 100644 --- a/wsapi/log.go +++ b/wsapi/log.go @@ -5,18 +5,18 @@ package wsapi import ( - "github.com/FactomProject/factomd/logger" + "github.com/FactomProject/factomd/log" ) // setup subsystem loggers var ( - rpcLog *logger.FLogger - serverLog *logger.FLogger - wsLog *logger.FLogger + rpcLog *log.FLogger + serverLog *log.FLogger + wsLog *log.FLogger ) func InitLogs(logPath, logLevel string) { - rpcLog = logger.NewLogFromConfig(logPath, logLevel, "RPC") - serverLog = logger.NewLogFromConfig(logPath, logLevel, "SERV") - wsLog = logger.NewLogFromConfig(logPath, logLevel, "WSAPI") + rpcLog = log.NewLogFromConfig(logPath, logLevel, "RPC") + serverLog = log.NewLogFromConfig(logPath, logLevel, "SERV") + wsLog = log.NewLogFromConfig(logPath, logLevel, "WSAPI") } diff --git a/wsapi/wsapi.go b/wsapi/wsapi.go index 2ccd22b4b9..ec5105cdc2 100644 --- a/wsapi/wsapi.go +++ b/wsapi/wsapi.go @@ -35,6 +35,7 @@ var Servers map[int]*web.Server var ServersMutex sync.Mutex func Start(state interfaces.IState) { + RegisterPrometheus() var server *web.Server ServersMutex.Lock() diff --git a/wsapi/wsapiV2.go b/wsapi/wsapiV2.go index 0fbb484a68..80dd13b7ab 100644 --- a/wsapi/wsapiV2.go +++ b/wsapi/wsapiV2.go @@ -12,6 +12,7 @@ import ( "net/http" "reflect" "strings" + "time" "github.com/FactomProject/factomd/common/constants" "github.com/FactomProject/factomd/common/entryBlock" @@ -27,6 +28,8 @@ import ( const API_VERSION string = "2.0" func HandleV2(ctx *web.Context) { + n := time.Now() + defer HandleV2APICallGeneral.Observe(float64(time.Since(n).Nanoseconds())) ServersMutex.Lock() state := ctx.Server.Env["state"].(interfaces.IState) ServersMutex.Unlock() @@ -168,6 +171,9 @@ func HandleV2Request(state interfaces.IState, j *primitives.JSON2Request) (*prim } func HandleV2DBlockByHeight(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallDBlockByHeight.Observe(float64(time.Since(n).Nanoseconds())) + heightRequest := new(HeightRequest) err := MapToObject(params, heightRequest) if err != nil { @@ -202,6 +208,9 @@ func HandleV2DBlockByHeight(state interfaces.IState, params interface{}) (interf } func HandleV2ECBlockByHeight(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallECBlockByHeight.Observe(float64(time.Since(n).Nanoseconds())) + heightRequest := new(HeightRequest) err := MapToObject(params, heightRequest) if err != nil { @@ -236,6 +245,9 @@ func HandleV2ECBlockByHeight(state interfaces.IState, params interface{}) (inter } func HandleV2FBlockByHeight(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallFblockByHeight.Observe(float64(time.Since(n).Nanoseconds())) + heightRequest := new(HeightRequest) err := MapToObject(params, heightRequest) if err != nil { @@ -270,6 +282,9 @@ func HandleV2FBlockByHeight(state interfaces.IState, params interface{}) (interf } func HandleV2ABlockByHeight(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallABlockByHeight.Observe(float64(time.Since(n).Nanoseconds())) + heightRequest := new(HeightRequest) err := MapToObject(params, heightRequest) if err != nil { @@ -348,6 +363,9 @@ func ObjectToJStruct(source interface{}) (*JStruct, error) { } func HandleV2CommitChain(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallCommitChain.Observe(float64(time.Since(n).Nanoseconds())) + commitChainMsg := new(MessageRequest) err := MapToObject(params, commitChainMsg) if err != nil { @@ -381,6 +399,9 @@ func HandleV2RevealChain(state interfaces.IState, params interface{}) (interface } func HandleV2CommitEntry(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallCommitEntry.Observe(float64(time.Since(n).Nanoseconds())) + commitEntryMsg := new(MessageRequest) err := MapToObject(params, commitEntryMsg) if err != nil { @@ -410,6 +431,9 @@ func HandleV2CommitEntry(state interfaces.IState, params interface{}) (interface } func HandleV2RevealEntry(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallRevealEntry.Observe(float64(time.Since(n).Nanoseconds())) + e := new(EntryRequest) err := MapToObject(params, e) if err != nil { @@ -439,6 +463,9 @@ func HandleV2RevealEntry(state interfaces.IState, params interface{}) (interface } func HandleV2DirectoryBlockHead(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallDBlockHead.Observe(float64(time.Since(n).Nanoseconds())) + h := new(DirectoryBlockHeadResponse) d := state.GetDirectoryBlockByHeight(state.GetHighestSavedBlk()) h.KeyMR = d.GetKeyMR().String() @@ -446,6 +473,9 @@ func HandleV2DirectoryBlockHead(state interfaces.IState, params interface{}) (in } func HandleV2RawData(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallRawData.Observe(float64(time.Since(n).Nanoseconds())) + hashkey := new(HashRequest) err := MapToObject(params, hashkey) if err != nil { @@ -499,6 +529,9 @@ func HandleV2RawData(state interfaces.IState, params interface{}) (interface{}, } func HandleV2Receipt(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallReceipt.Observe(float64(time.Since(n).Nanoseconds())) + hashkey := new(HashRequest) err := MapToObject(params, hashkey) if err != nil { @@ -524,6 +557,9 @@ func HandleV2Receipt(state interfaces.IState, params interface{}) (interface{}, } func HandleV2DirectoryBlock(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallDBlock.Observe(float64(time.Since(n).Nanoseconds())) + keymr := new(KeyMRRequest) err := MapToObject(params, keymr) if err != nil { @@ -561,6 +597,9 @@ func HandleV2DirectoryBlock(state interfaces.IState, params interface{}) (interf } func HandleV2EntryBlock(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallEblock.Observe(float64(time.Since(n).Nanoseconds())) + keymr := new(KeyMRRequest) err := MapToObject(params, keymr) if err != nil { @@ -630,6 +669,9 @@ func HandleV2EntryBlock(state interfaces.IState, params interface{}) (interface{ } func HandleV2Entry(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallEntry.Observe(float64(time.Since(n).Nanoseconds())) + hashkey := new(HashRequest) err := MapToObject(params, hashkey) if err != nil { @@ -669,6 +711,9 @@ func HandleV2Entry(state interfaces.IState, params interface{}) (interface{}, *p } func HandleV2ChainHead(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallChainHead.Observe(float64(time.Since(n).Nanoseconds())) + chainid := new(ChainIDRequest) err := MapToObject(params, chainid) if err != nil { @@ -710,6 +755,9 @@ func HandleV2ChainHead(state interfaces.IState, params interface{}) (interface{} } func HandleV2EntryCreditBalance(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallECBal.Observe(float64(time.Since(n).Nanoseconds())) + ecadr := new(AddressRequest) err := MapToObject(params, ecadr) if err != nil { @@ -744,6 +792,9 @@ func HandleV2EntryCreditBalance(state interfaces.IState, params interface{}) (in } func HandleV2EntryCreditRate(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallECRate.Observe(float64(time.Since(n).Nanoseconds())) + resp := new(EntryCreditRateResponse) resp.Rate = int64(state.GetPredictiveFER()) @@ -751,6 +802,9 @@ func HandleV2EntryCreditRate(state interfaces.IState, params interface{}) (inter } func HandleV2FactoidSubmit(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallFctTx.Observe(float64(time.Since(n).Nanoseconds())) + t := new(TransactionRequest) err := MapToObject(params, t) if err != nil { @@ -781,6 +835,9 @@ func HandleV2FactoidSubmit(state interfaces.IState, params interface{}) (interfa } func HandleV2FactoidBalance(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallFABal.Observe(float64(time.Since(n).Nanoseconds())) + fadr := new(AddressRequest) err := MapToObject(params, fadr) if err != nil { @@ -811,6 +868,9 @@ func HandleV2FactoidBalance(state interfaces.IState, params interface{}) (interf } func HandleV2Heights(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallHeights.Observe(float64(time.Since(n).Nanoseconds())) + h := new(HeightsResponse) h.DirectoryBlockHeight = int64(state.GetHighestSavedBlk()) @@ -825,6 +885,9 @@ func HandleV2Heights(state interfaces.IState, params interface{}) (interface{}, } func HandleV2GetPendingEntries(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallPendingEntries.Observe(float64(time.Since(n).Nanoseconds())) + chainid := new(ChainIDRequest) err := MapToObject(params, chainid) if err != nil { @@ -836,6 +899,9 @@ func HandleV2GetPendingEntries(state interfaces.IState, params interface{}) (int } func HandleV2GetPendingTransactions(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallPendingTxs.Observe(float64(time.Since(n).Nanoseconds())) + fadr := new(AddressRequest) err := MapToObject(params, fadr) if err != nil { @@ -848,6 +914,9 @@ func HandleV2GetPendingTransactions(state interfaces.IState, params interface{}) } func HandleV2Properties(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallProp.Observe(float64(time.Since(n).Nanoseconds())) + vtos := func(f int) string { v0 := f / 1000000000 v1 := (f % 1000000000) / 1000000 @@ -864,6 +933,9 @@ func HandleV2Properties(state interfaces.IState, params interface{}) (interface{ } func HandleV2SendRawMessage(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallSendRaw.Observe(float64(time.Since(n).Nanoseconds())) + r := new(SendRawMessageRequest) err := MapToObject(params, r) if err != nil { @@ -888,6 +960,9 @@ func HandleV2SendRawMessage(state interfaces.IState, params interface{}) (interf } func HandleV2GetTranasction(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallTransaction.Observe(float64(time.Since(n).Nanoseconds())) + hashkey := new(HashRequest) err := MapToObject(params, hashkey) if err != nil { @@ -980,6 +1055,9 @@ func HandleV2GetTranasction(state interfaces.IState, params interface{}) (interf } func HandleV2TransactionRate(state interfaces.IState, params interface{}) (interface{}, *primitives.JSONError) { + n := time.Now() + defer HandleV2APICallTpsRate.Observe(float64(time.Since(n).Nanoseconds())) + r := new(TransactionRateResponse) // total : Transaction rate over entire life of node diff --git a/wsapi/wsapiV2_test.go b/wsapi/wsapiV2_test.go index 46f839358b..34a44b844e 100644 --- a/wsapi/wsapiV2_test.go +++ b/wsapi/wsapiV2_test.go @@ -15,6 +15,11 @@ import ( . "github.com/FactomProject/factomd/wsapi" ) +func TestRegisterPrometheus(t *testing.T) { + RegisterPrometheus() + RegisterPrometheus() +} + func TestHandleV2GetRaw(t *testing.T) { type RawData struct { Hash1 string