diff --git a/pkg/peerprotocol/fullnode.go b/pkg/peerprotocol/fullnode.go index cc9a9e7..52a7f1d 100644 --- a/pkg/peerprotocol/fullnode.go +++ b/pkg/peerprotocol/fullnode.go @@ -20,3 +20,9 @@ func NewFullNodeProtocol(connection *Connection) (*FullNodeProtocol, error) { func (c *FullNodeProtocol) RequestPeers() error { return c.connection.Do(protocols.ProtocolMessageTypeRequestPeers, &protocols.RequestPeers{}) } + +// RequestBlock asks the current peer to respond with a block +func (c *FullNodeProtocol) RequestBlock(heigth uint32, includeTransactionBlock bool) error { + return c.connection.Do(protocols.ProtocolMessageTypeRequestBlock, &protocols.RequestBlock{Height: heigth, IncludeTransactionBlock: includeTransactionBlock}) +} + diff --git a/pkg/protocols/fullnode.go b/pkg/protocols/fullnode.go index 7cfad67..030d46b 100644 --- a/pkg/protocols/fullnode.go +++ b/pkg/protocols/fullnode.go @@ -11,3 +11,24 @@ type RequestPeers struct{} type RespondPeers struct { PeerList []types.TimestampedPeerInfo `streamable:""` } + +// NewPeek is the format for the new_peak response +type NewPeek struct { + HeaderHash types.Bytes32 `streamable:""` + Height uint32 `streamable:""` + Weight types.Uint128 `streamable:""` + ForkPointWithPreviousPeak uint32 `streamable:""` + UnfinishedRewardBlockHash types.Bytes32 `streamable:""` +} + +// RequestBlock is the format for the request_block request +type RequestBlock struct { + Height uint32 `streamable:""` + IncludeTransactionBlock bool `streamable:""` +} + +// RespondBlock is the format for the respond_block response +type RespondBlock struct { + Block types.FullBlock `streamable:""` +} + diff --git a/pkg/protocols/fullnode_test.go b/pkg/protocols/fullnode_test.go index 3e996d8..d012e27 100644 --- a/pkg/protocols/fullnode_test.go +++ b/pkg/protocols/fullnode_test.go @@ -38,3 +38,24 @@ func TestRespondPeers(t *testing.T) { assert.NoError(t, err) assert.Equal(t, encodedBytes, reencodedBytes) } + +func TestRequestBlock(t *testing.T) { + blockHeight := uint32(2347) + hexStr := "00000000000000000000000000000000000040340000092b000000000000000000000001adcac4c82bc188b377ded9d77e4ba90ec871f9c0b8e4798a1d23a65900e0c10d3d9bd31d291abb6b4c6ed7c151032ddf690745b22d2f6f10f2c0ac3f8e14f5150e18f115f00182fd324dffdf8c57792e9d26adc9841ea0d8214afae9f44c5bd3f06af06bcaee17dca035be794c36c423288cef2fdf5900b66a2f7e9c163da148e9101ca260b084f678b4ae0ca5772b5750b7f44394efcfc351800f21eac65d900ddebbbf8e47732000000100b18870d5b5fcd7baa9445c1f8458beac4dcd10fa9254646a5c6b11d018be82a803487d8518448052600935b18943f7e7db758db3ac1631018e3d18336505e452c768506d44094bf209c2b015ff2cedfd7aa6e9256e13b4652adb8c1452c501a933404761806f4e11ce2e138602fb17ee0383c071b8fb7f47679501b77a1d53c18093ebf53269a43650bb78737e3da4150d34927b5c3cfd994d77b34f061376d104e64dfafbf9eb313f0b0aa88059b7d95af5c8ee0e4ea4737a7172109fa990e5587917e5c6b07237de592d4309c319095311b920cf5963c4e7243c9de9fc3d3cadc69bfcc503fe28551e6886f4a308cc9383eb552516908323ca4bb7a006c70d01c188b377ded9d77e4ba90ec871f9c0b8e4798a1d23a65900e0c10d3d9bd31d2900000000056000000200ae256619f34f64c60b9c3821ea69e178cc601e49cc4b246176c17c51ed694b8584da0393e82142029f717214e0b44a31aaaf9875f27079f40de19ddec6b3360269a5161c2f2769f675f8e7ce8b457fa7bcc0a86e1f9330b9dd4e559b38137a0d010083b3a189e345f2af24515e39ea92b13bacd6dd8c14b257725bd93cdbb63ea88f135e3acf7e6ae5b960a6cd8db0cb3d020512990bba2ce011ea58cad1262b28e0db72e4309c7a2695908bfa3aefeb89335407626f7da8dbe10ca6f926de15a981c188b377ded9d77e4ba90ec871f9c0b8e4798a1d23a65900e0c10d3d9bd31d290000000005cac4c80100424506b6b6957111f455a922fcc02a6456170c2d2a9a77a58f35d95bc14e0d91be5efb9d73b4c3142b2d9ca8445697164088fb2f3b7569239820e62166c58b2009ac1c20347fec68b93fbeba55080cdf09b671b17b29360464a22785fff5b81a010001103b14520e67df9edf58dcd3028c37d779b20e2a9a1d8870cd8d34b81305e92d0000000000028f1202001d872926b6b4512e9f86240409a4dcb529a789f33bd45d5c3403d8557d20731122d457feb0a4c2ba79411e4213a14a9437a4fa71c88bdf2f60085c0a6b154f2790325aeade83148ce0138a1df6479010ad831be24942d2effeca810d0f1baf37020195489a1064ce6366a92284e10eb4bacd284f7d7e085ecd62782fc7b1703d4bd395f65a469c5c37ae8a16b0756a1f5ad80407bf1c588749e90f68150ef14a407551eecccc329849f48e161aebf55d7085129e307588d1853e5e037fab7259df29cf4d9ef9dac40874f3a589f8eeaf10825bcb9e9b90074adc72cf6f3382ae65e900000000001b78cf00003e7378f866dabdd35ee5e2c5cfb7115b725c2e90df25dbba2590ff6d4b4e56feebdc774d01aee49e95e529e6979e6f47259c3611b3396fbdca3a2c4dd82fac68836eee4a71bc7f75a12f3febcdc1f80bf201b2ff5b9d994df9c3fda9df0a5b17010001adaa67a9a870fd370fe16fcbb65c4be294dfb6579600323d6e1e597b11256dee00000000001b78cf03005e37c168d5761771d986f4766c6972ef9b0653f3adf917d335e7cefd790cbecddecf021f9df463a6a801e8eedc73aa569f5c027bdec416813f2ff334c88fe72051f1568e27811b011d35da29099004eb09ba6a038844832bd725009d18a1b81701000001000000006403002c6a887316e81f4273ab2beb3ac84f31825a31f4afd0e723545b75acea10caf64b5dda4ddffa13b985086b2ee88fa5a1d4748d34f9b8ccd8b40418a8291d3e0491b4ad5433d7e9e1902f9e73398cff543372d6ba60bf07c21161e236da73f7010e05010000000064000057b0969c023691431262bd495c50cb005f3388a4750566a98b35761827e5f94a5673cb0bd89013313db595e51dc0c19cd3b426806d79420320f4e84031bd3f0befba686e74483ddc8ea239d27a37048474a09dd920d0b5f2fb91571f63d7762401000101020000017e0000d09482e77fb1dcf7c0229a46692a4f38f991688149f676a876ba0c3e166f3994646381a763404f5584d76283ecafc2fe678a0a81b1e75e73a36d19159182600395ac30168044f5f427071e76d97dd4b70eab06be6fa3ac24793a4488780e6509010000000000000091b4ab6fab4f3e3f7912e28e385a195a4a4e65b325bfbe313114da885eaf74e642179d0200889b70e2b2032c4d44406a4e9177f4158fb7609b930876137437ef3de3af1c8e7e8500273ab77a3f6e40cceccd5d491ac291ec8af137c100aff9504007e1de0aeb572d73cb348d529da8defef95b8bd815e2f2814b7b528596fb1a6a48f26b130100000000000001b454bca04db73ba1c4c19c470866e511a71a73600837ac8467cba42b57705132b16c050100908b4e5520fdf9edaaf02fa81e54a1b59aacc90195ed7f96456b56f5d606b2d70d99f3270c8de66a19526d221f1b003e23e4b90def8b62f2c764861c61d724779fa7d397e5ab91c30106fe4957e674a3d2ff65c06d1808a91b32c23c08c9c312010000020000017e02001f906ed8f095bad9d70c3d5b792b7b3d6a50ee8f6f3fab32876e99910d4be748f01e450030e237dc7aef7380d266179a98a6305125e1f3ac2cacab93dde0402e6e4ae45d445d9cbe59026d1229cf241bd7e8486dcf3d20a2d2f01e48370ce14502010000000000061ae4907e5e332380a2689e77ea9a485fbb5d6a3dd84c5aa179d3d9a003fecc43f35b270300aca0158c98ff464f7942257f901ea60f2a95ad1f9376c17b42a0bba97bd86e5b65ed9e93526334c6ed0ceb1b2032d2af24547573569c2801fb2d8f42b0ad8c5daff72f0bc62dc2503f9385f00c719a7ddf9261166fef6114afbea3966ce1af050100000000000012504890a945b69bc719b5cc9c5059a348d5034c5401f0267d6a1128837178a10b3afce301002b418db30870754b5d0abaa3a4fd1060591a85ff964997ca4d29e42a71d019c22e1e025397642a89c0e13bb1f742c5ed97de55f97c0ab63e640e10be2b2ebe1bcbd629b8906cbb0420a1f5ab1583090ec08a341217d85756ccf2815846f7fd2102010001020000017e02009a50a6536d487c9a7fad852bc3ab9ed2dca8d633164a647f2cdf20468fb02ab4445dd6ce85d16641aee46e1340a530e0917caadc0f5ec9fc7fc1b928698c4a12ff306deb3689d18bf6c90af772bba1151cb1b82c4f319b86b0def633bd80c51805000000000000061ae4d251d5e5e51661e483dd39862ecfbed6ef3a48529b2b522a431c67862d9bb6548b0200d42a2de42e351a2b5b0c3e9aa3adc3806033f233147e1cc8e77984601e2fdb888712f45bdec670e65b9a38825f0e84a91ae4606b548a55a19cb4336dd2e5b569ff6867f1c28714cfb06314849e55896a025ae0d57c751864b5c8ca1d35e3245701000000000000125048e8c5405ea099da345f6f8fe9d092f9ea51b6a7fe48b37be3891b05c051db9393e101006a30c805895b64dc89886fe508c3c3f0fa2a25e4bc17f9be12a37ec32e03ce7c5548130ff2bc98e8a19452e5da259ba95b1c3b7e687463dc70e704948b9709097331606e05dbd94aea96017668943475f160cc3e91de3448dde4ed81cec2d0130100008416fadbf6c13544d2be22bf69ac81679c237e3852972af2694c1c26466025ce2a0b38d5feda302fb27579e85bbe01ae70ea0871d20601d0ed7f3abc25ebf9989825bc9b4470bf03fe10007531435dc7b5332552800aa120b36f3baa02abf9e1cea41d2961b6132fcaea28ca35b425cb9bb2a23c71fa8af3c44509dfbb4e64670000000001aee44571244f660c4c22e0e2f85d51c2bbdd75e8217e04afa159b603739aa0ab133cae0ef53fbcb5a2ad59cd976bffdf18cd2cd96c64790c9a11549a873e99db22e6ce56e2105ea2c37a6d1cafc1cfc3cc03bdcc8eea62bee18ed99de59fb828cea41d2961b6132fcaea28ca35b425cb9bb2a23c71fa8af3c44509dfbb4e64670000000000000000000000000000000000000000000000000000000003a2c7c9b14fca84c30a94fddee8dbd01327103f6f3db7b45ed493603ff39edd2c0702c1404ebdbf1216619572fe54d4320919a60be60dea18928fbc108e638a7edf8df714ce1b708dada423602913cd59be9e018dc128ed51c32e959e87a2a3e603cb49000000000000000000" + + // Hex to bytes + encodedBytes, err := hex.DecodeString(hexStr) + assert.NoError(t, err) + + rb := &protocols.RespondBlock{} + + err = streamable.Unmarshal(encodedBytes, rb) + assert.NoError(t, err) + + assert.Equal(t, rb.Block.RewardChainBlock.Height, blockHeight) + + // Test going the other direction + reencodedBytes, err := streamable.Marshal(rb) + assert.NoError(t, err) + assert.Equal(t, encodedBytes, reencodedBytes) +} diff --git a/pkg/protocols/messagetypes.go b/pkg/protocols/messagetypes.go index 2bfc4cb..eea20f7 100644 --- a/pkg/protocols/messagetypes.go +++ b/pkg/protocols/messagetypes.go @@ -9,6 +9,15 @@ const ( // there are many more of these in Chia - only listing the ones current is use for now + // ProtocolMessageTypeNewPeak new_peak + ProtocolMessageTypeNewPeak ProtocolMessageType = 20 + + // ProtocolMessageTypeRequestBlock request_block + ProtocolMessageTypeRequestBlock ProtocolMessageType = 26 + + // ProtocolMessageTypeRespondBlock respond_block + ProtocolMessageTypeRespondBlock ProtocolMessageType = 27 + // ProtocolMessageTypeRequestPeers request_peers ProtocolMessageTypeRequestPeers ProtocolMessageType = 43 diff --git a/pkg/streamable/streamable.go b/pkg/streamable/streamable.go index 1c465b0..dc0416c 100644 --- a/pkg/streamable/streamable.go +++ b/pkg/streamable/streamable.go @@ -60,60 +60,11 @@ func unmarshalStruct(bytes []byte, t reflect.Type, tv reflect.Value) ([]byte, er return bytes, nil } -func unmarshalSlice(bytes []byte, t reflect.Type, v reflect.Value) ([]byte, error) { - var err error - var newVal []byte - - // Slice/List is 4 byte prefix (number of items) and then serialization of each item - // Get 4 byte length prefix - var length []byte - length, bytes, err = util.ShiftNBytes(4, bytes) - if err != nil { - return nil, err - } - numItems := binary.BigEndian.Uint32(length) - - sliceKind := t.Elem().Kind() - switch sliceKind { - case reflect.Uint8: // same as byte - // In this case, numItems == numBytes, because its a uint8 - newVal, bytes, err = util.ShiftNBytes(uint(numItems), bytes) - if err != nil { - return bytes, err - } - if !v.CanSet() { - return bytes, fmt.Errorf("field %s is not settable", v.String()) - } - - sliceReflect := reflect.MakeSlice(v.Type(), 0, 0) - for _, newValBytes := range newVal { - sliceReflect = reflect.Append(sliceReflect, reflect.ValueOf(newValBytes)) - } - v.Set(sliceReflect) - case reflect.Struct: - sliceReflect := reflect.MakeSlice(v.Type(), 0, 0) - for j := uint32(0); j < numItems; j++ { - newValue := reflect.Indirect(reflect.New(v.Type().Elem())) - bytes, err = unmarshalStruct(bytes, t.Elem(), newValue) - if err != nil { - return nil, err - } - sliceReflect = reflect.Append(sliceReflect, newValue) - } - v.Set(sliceReflect) - default: - return bytes, fmt.Errorf("encountered type inside slice that is not implemented") - } - - return bytes, nil -} - // Struct field is used to parse out the streamable tag // Not needed for anything else // When recursively calling this on a wrapper type like mo.Option, pass the parent/wrapping StructField func unmarshalField(bytes []byte, fieldType reflect.Type, fieldValue reflect.Value, structField reflect.StructField) ([]byte, error) { - var tagPresent bool - if _, tagPresent = structField.Tag.Lookup(tagName); !tagPresent { + if _, tagPresent := structField.Tag.Lookup(tagName); !tagPresent { // Continuing because the tag isn't present return bytes, nil } @@ -127,7 +78,7 @@ func unmarshalField(bytes []byte, fieldType reflect.Type, fieldValue reflect.Val var presentFlag []byte presentFlag, bytes, err = util.ShiftNBytes(1, bytes) if err != nil { - return nil, err + return bytes, err } if presentFlag[0] == boolFalse { return bytes, nil @@ -201,27 +152,65 @@ func unmarshalField(bytes []byte, fieldType reflect.Type, fieldValue reflect.Val } newInt := util.BytesToUint64(newVal) fieldValue.SetUint(newInt) + case reflect.Array: + switch fieldValue.Type().Elem().Kind() { + case reflect.Uint8: + for i := 0; i < fieldValue.Len(); i++ { + optionalField := fieldValue.Index(1) + optionalType := optionalField.Type() + bytes, err = unmarshalField(bytes, optionalType, fieldValue.Index(i), structField) + if err != nil { + return bytes, err + } + } + } case reflect.Slice: - bytes, err = unmarshalSlice(bytes, fieldType, fieldValue) + var length []byte + length, bytes, err = util.ShiftNBytes(4, bytes) if err != nil { return bytes, err } + numItems := binary.BigEndian.Uint32(length) + + sliceReflect := reflect.MakeSlice(fieldValue.Type(), 0, 0) + for j := uint32(0); j < numItems; j++ { + newValue := reflect.Indirect(reflect.New(fieldValue.Type().Elem())) + bytes, err = unmarshalField(bytes, fieldType.Elem(), newValue, structField) + if err != nil { + return bytes, err + } + sliceReflect = reflect.Append(sliceReflect, newValue) + } + + fieldValue.Set(sliceReflect) case reflect.String: // 4 byte size prefix, then []byte which can be converted to utf-8 string // Get 4 byte length prefix var length []byte length, bytes, err = util.ShiftNBytes(4, bytes) if err != nil { - return nil, err + return bytes, err } numBytes := binary.BigEndian.Uint32(length) var strBytes []byte strBytes, bytes, err = util.ShiftNBytes(uint(numBytes), bytes) if err != nil { - return nil, err + return bytes, err } fieldValue.SetString(string(strBytes)) + case reflect.Struct: + bytes, err = unmarshalStruct(bytes, fieldType, fieldValue) + if err != nil { + return bytes, err + } + case reflect.Bool: + var boolByte []byte + boolByte, bytes, err = util.ShiftNBytes(1, bytes) + if err != nil { + return bytes, err + } + fieldValue.SetBool(boolByte[0] == boolTrue) default: return bytes, fmt.Errorf("unimplemented type %s", fieldValue.Kind()) } @@ -311,6 +300,24 @@ func marshalField(finalBytes []byte, fieldType reflect.Type, fieldValue reflect. finalBytes = append(finalBytes, util.Uint32ToBytes(newInt)...) case reflect.Uint64: finalBytes = append(finalBytes, util.Uint64ToBytes(fieldValue.Uint())...) + case reflect.Array: + switch fieldType.Elem().Kind() { + case reflect.Uint8: + // special case as byte-string + for i := 0; i < fieldValue.Len(); i++ { + finalBytes, err = marshalField(finalBytes, fieldType.Elem(), fieldValue.Index(i), structField) + if err != nil { + return finalBytes, err + } + } + default: + return finalBytes, fmt.Errorf("unimplemented type %s", fieldType.Elem().Kind()) + } + case reflect.Struct: + finalBytes, err = marshalStruct(finalBytes, fieldType, fieldValue) + if err != nil { + return finalBytes, err + } case reflect.Slice: finalBytes, err = marshalSlice(finalBytes, fieldType, fieldValue) if err != nil { @@ -323,6 +330,12 @@ func marshalField(finalBytes []byte, fieldType reflect.Type, fieldValue reflect. finalBytes = append(finalBytes, util.Uint32ToBytes(numBytes)...) finalBytes = append(finalBytes, strBytes...) + case reflect.Bool: + if fieldValue.Bool() { + finalBytes = append(finalBytes, boolTrue) + } else { + finalBytes = append(finalBytes, boolFalse) + } default: return finalBytes, fmt.Errorf("unimplemented type %s", fieldValue.Kind()) } @@ -344,7 +357,7 @@ func marshalSlice(finalBytes []byte, t reflect.Type, v reflect.Value) ([]byte, e // This is the easy case - already a slice of bytes finalBytes = append(finalBytes, v.Bytes()...) case reflect.Struct: - for j := 0; j < v.Len(); j++ { + for j := 0; j < int(numItems); j++ { currentStruct := v.Index(j) finalBytes, err = marshalStruct(finalBytes, currentStruct.Type(), currentStruct) diff --git a/pkg/types/block.go b/pkg/types/block.go index 4329cfa..9afde21 100644 --- a/pkg/types/block.go +++ b/pkg/types/block.go @@ -43,40 +43,38 @@ type BlockRecord struct { // FullBlock a full block // https://github.com/Chia-Network/chia-blockchain/blob/0befdec071f49708e26c7638656874492c52600a/chia/types/full_block.py#L16 -// @TODO Streamable type FullBlock struct { - FinishedSubSlots []EndOfSubSlotBundle `json:"finished_sub_slots"` - RewardChainBlock RewardChainBlock `json:"reward_chain_block"` - ChallengeChainSPProof mo.Option[VDFProof] `json:"challenge_chain_sp_proof"` - ChallengeChainIPProof VDFProof `json:"challenge_chain_ip_proof"` - RewardChainSPProof mo.Option[VDFProof] `json:"reward_chain_sp_proof"` - RewardChainIPProof VDFProof `json:"reward_chain_ip_proof"` - InfusedChallengeChainIPProof mo.Option[VDFProof] `json:"infused_challenge_chain_ip_proof"` - Foliage Foliage `json:"foliage"` - FoliageTransactionBlock mo.Option[FoliageTransactionBlock] `json:"foliage_transaction_block"` - TransactionsInfo mo.Option[TransactionsInfo] `json:"transactions_info"` - TransactionsGenerator mo.Option[SerializedProgram] `json:"transactions_generator"` - TransactionsGeneratorRefList []uint32 `json:"transactions_generator_ref_list"` + FinishedSubSlots []EndOfSubSlotBundle `json:"finished_sub_slots" streamable:""` + RewardChainBlock RewardChainBlock `json:"reward_chain_block" streamable:""` + ChallengeChainSPProof mo.Option[VDFProof] `json:"challenge_chain_sp_proof" streamable:""` + ChallengeChainIPProof VDFProof `json:"challenge_chain_ip_proof" streamable:""` + RewardChainSPProof mo.Option[VDFProof] `json:"reward_chain_sp_proof" streamable:""` + RewardChainIPProof VDFProof `json:"reward_chain_ip_proof" streamable:""` + InfusedChallengeChainIPProof mo.Option[VDFProof] `json:"infused_challenge_chain_ip_proof" streamable:""` + Foliage Foliage `json:"foliage" streamable:""` + FoliageTransactionBlock mo.Option[FoliageTransactionBlock] `json:"foliage_transaction_block" streamable:""` + TransactionsInfo mo.Option[TransactionsInfo] `json:"transactions_info" streamable:""` + TransactionsGenerator mo.Option[SerializedProgram] `json:"transactions_generator" streamable:""` + TransactionsGeneratorRefList []uint32 `json:"transactions_generator_ref_list" streamable:""` } // RewardChainBlock Reward Chain Block // https://github.com/Chia-Network/chia-blockchain/blob/0befdec071f49708e26c7638656874492c52600a/chia/types/blockchain_format/reward_chain_block.py#L30 -// @TODO Streamable type RewardChainBlock struct { - Weight Uint128 `json:"weight"` - Height uint32 `json:"height"` - TotalIters Uint128 `json:"total_iters"` - SignagePointIndex uint8 `json:"signage_point_index"` - POSSSCCChallengeHash Bytes32 `json:"pos_ss_cc_challenge_hash"` - ProofOfSpace ProofOfSpace `json:"proof_of_space"` - ChallengeChainSPVDF mo.Option[VDFInfo] `json:"challenge_chain_sp_vdf"` - ChallengeChainSPSignature G2Element `json:"challenge_chain_sp_signature"` - ChallengeChainIPVDF VDFInfo `json:"challenge_chain_ip_vdf"` - RewardChainSPVDF mo.Option[VDFInfo] `json:"reward_chain_sp_vdf"` // Not present for first sp in slot - RewardChainSPSignature G2Element `json:"reward_chain_sp_signature"` - RewardChainIPVDF VDFInfo `json:"reward_chain_ip_vdf"` - InfusedChallengeChainIPVDF mo.Option[VDFInfo] `json:"infused_challenge_chain_ip_vdf"` // Iff deficit < 16 - IsTransactionBlock bool `json:"is_transaction_block"` + Weight Uint128 `json:"weight" streamable:""` + Height uint32 `json:"height" streamable:""` + TotalIters Uint128 `json:"total_iters" streamable:""` + SignagePointIndex uint8 `json:"signage_point_index" streamable:""` + POSSSCCChallengeHash Bytes32 `json:"pos_ss_cc_challenge_hash" streamable:""` + ProofOfSpace ProofOfSpace `json:"proof_of_space" streamable:""` + ChallengeChainSPVDF mo.Option[VDFInfo] `json:"challenge_chain_sp_vdf" streamable:""` + ChallengeChainSPSignature G2Element `json:"challenge_chain_sp_signature" streamable:""` + ChallengeChainIPVDF VDFInfo `json:"challenge_chain_ip_vdf" streamable:""` + RewardChainSPVDF mo.Option[VDFInfo] `json:"reward_chain_sp_vdf" streamable:""` // Not present for first sp in slot + RewardChainSPSignature G2Element `json:"reward_chain_sp_signature" streamable:""` + RewardChainIPVDF VDFInfo `json:"reward_chain_ip_vdf" streamable:""` + InfusedChallengeChainIPVDF mo.Option[VDFInfo] `json:"infused_challenge_chain_ip_vdf" streamable:""` // Iff deficit < 16 + IsTransactionBlock bool `json:"is_transaction_block" streamable:""` } // BlockCountMetrics metrics from get_block_count_metrics endpoint diff --git a/pkg/types/classgroup.go b/pkg/types/classgroup.go index 7d62f31..d6cf284 100644 --- a/pkg/types/classgroup.go +++ b/pkg/types/classgroup.go @@ -2,7 +2,6 @@ package types // ClassgroupElement Classgroup Element // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/classgroup.py#L12 -// @TODO Streamable type ClassgroupElement struct { - Data Bytes100 `json:"data"` + Data Bytes100 `json:"data" streamable:""` } diff --git a/pkg/types/coin.go b/pkg/types/coin.go index 7185220..ee3f980 100644 --- a/pkg/types/coin.go +++ b/pkg/types/coin.go @@ -8,9 +8,9 @@ import ( // Coin is a coin // https://github.com/Chia-Network/chia_rs/blob/69908769e7df0ff2c10569aea9992cfecf3eb23a/wheel/src/coin.rs#L16 type Coin struct { - ParentCoinInfo Bytes32 `json:"parent_coin_info"` - PuzzleHash Bytes32 `json:"puzzle_hash"` - Amount uint64 `json:"amount"` + ParentCoinInfo Bytes32 `json:"parent_coin_info" streamable:""` + PuzzleHash Bytes32 `json:"puzzle_hash" streamable:""` + Amount uint64 `json:"amount" streamable:""` } // ID returns the coin ID of the coin diff --git a/pkg/types/endofslotbundle.go b/pkg/types/endofslotbundle.go index e7954ae..604503b 100644 --- a/pkg/types/endofslotbundle.go +++ b/pkg/types/endofslotbundle.go @@ -1,8 +1,12 @@ package types +import "github.com/samber/mo" + // EndOfSubSlotBundle end of subslot bundle // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/end_of_slot_bundle.py#L17 -// @TODO Streamable type EndOfSubSlotBundle struct { - // @TODO + ChallengeChain ChallengeChainSubSlot `json:"challenge_chain" streamable:""` + InfusedChallengeChain mo.Option[InfusedChallengeChainSubSlot] `json:"infused_challenge_chain" streamable:""` + RewardChain RewardChainSubSlot `json:"reward_chain" streamable:""` + Proofs SubSlotProofs `json:"proofs" streamable:""` } diff --git a/pkg/types/foliage.go b/pkg/types/foliage.go index 1143869..7a61cec 100644 --- a/pkg/types/foliage.go +++ b/pkg/types/foliage.go @@ -6,47 +6,44 @@ import ( // FoliageBlockData FoliageBlockData // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/foliage.py#L41 -// @TODO Streamable type FoliageBlockData struct { - UnfinishedRewardBlockHash Bytes32 `json:"unfinished_reward_block_hash"` - PoolTarget PoolTarget `json:"pool_target"` - PoolSignature mo.Option[G2Element] `json:"pool_signature"` - FarmerRewardPuzzleHash Bytes32 `json:"farmer_reward_puzzle_hash"` - ExtensionData Bytes32 `json:"extension_data"` + UnfinishedRewardBlockHash Bytes32 `json:"unfinished_reward_block_hash" streamable:""` + PoolTarget PoolTarget `json:"pool_target" streamable:""` + PoolSignature mo.Option[G2Element] `json:"pool_signature" streamable:""` + FarmerRewardPuzzleHash Bytes32 `json:"farmer_reward_puzzle_hash" streamable:""` + ExtensionData Bytes32 `json:"extension_data" streamable:""` } // Foliage Foliage // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/foliage.py#L52 -// @TODO Streamable type Foliage struct { - PrevBlockHash Bytes32 `json:"prev_block_hash"` - RewardBlockHash Bytes32 `json:"reward_block_hash"` - FoliageBlockData FoliageBlockData `json:"foliage_block_data"` - FoliageBlockDataSignature G2Element `json:"foliage_block_data_signature"` - FoliageTransactionBlockHash mo.Option[Bytes32] `json:"foliage_transaction_block_hash"` - FoliageTransactionBlockSignature mo.Option[G2Element] `json:"foliage_transaction_block_signature"` + PrevBlockHash Bytes32 `json:"prev_block_hash" streamable:""` + RewardBlockHash Bytes32 `json:"reward_block_hash" streamable:""` + FoliageBlockData FoliageBlockData `json:"foliage_block_data" streamable:""` + FoliageBlockDataSignature G2Element `json:"foliage_block_data_signature" streamable:""` + FoliageTransactionBlockHash mo.Option[Bytes32] `json:"foliage_transaction_block_hash" streamable:""` + FoliageTransactionBlockSignature mo.Option[G2Element] `json:"foliage_transaction_block_signature" streamable:""` } // FoliageTransactionBlock foliage transaction block // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/foliage.py#L29 -// @TODO Streamable type FoliageTransactionBlock struct { - PrevTransactionBlockHash Bytes32 `json:"prev_transaction_block_hash"` - Timestamp Timestamp `json:"timestamp"` - FilterHash Bytes32 `json:"filter_hash"` - AdditionsRoot Bytes32 `json:"additions_root"` - RemovalsRoot Bytes32 `json:"removals_root"` - TransactionsInfoHash Bytes32 `json:"transactions_info_hash"` + PrevTransactionBlockHash Bytes32 `json:"prev_transaction_block_hash" streamable:""` + Timestamp uint64 `json:"timestamp" streamable:""` + FilterHash Bytes32 `json:"filter_hash" streamable:""` + AdditionsRoot Bytes32 `json:"additions_root" streamable:""` + RemovalsRoot Bytes32 `json:"removals_root" streamable:""` + TransactionsInfoHash Bytes32 `json:"transactions_info_hash" streamable:""` } // TransactionsInfo transactions info // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/foliage.py#L17 // @TODO Streamable type TransactionsInfo struct { - GeneratorRoot Bytes32 `json:"generator_root"` - GeneratorRefsRoot Bytes32 `json:"generator_refs_root"` - AggregatedSignature G2Element `json:"aggregated_signature"` - Fees uint64 `json:"fees"` - Cost uint64 `json:"cost"` - RewardClaimsIncorporated []Coin `json:"reward_claims_incorporated"` + GeneratorRoot Bytes32 `json:"generator_root" streamable:""` + GeneratorRefsRoot Bytes32 `json:"generator_refs_root" streamable:""` + AggregatedSignature G2Element `json:"aggregated_signature" streamable:""` + Fees uint64 `json:"fees" streamable:""` + Cost uint64 `json:"cost" streamable:""` + RewardClaimsIncorporated []Coin `json:"reward_claims_incorporated" streamable:""` } diff --git a/pkg/types/pooltarget.go b/pkg/types/pooltarget.go index 52682ba..24e54d2 100644 --- a/pkg/types/pooltarget.go +++ b/pkg/types/pooltarget.go @@ -2,8 +2,7 @@ package types // PoolTarget PoolTarget // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/pool_target.py#L12 -// @TODO Streamable type PoolTarget struct { - PuzzleHash Bytes32 `json:"puzzle_hash"` - MaxHeight uint32 `json:"max_height"` + PuzzleHash Bytes32 `json:"puzzle_hash" streamable:""` + MaxHeight uint32 `json:"max_height" streamable:""` } diff --git a/pkg/types/proofofspace.go b/pkg/types/proofofspace.go index 9a6edda..c6d4a66 100644 --- a/pkg/types/proofofspace.go +++ b/pkg/types/proofofspace.go @@ -6,12 +6,11 @@ import ( // ProofOfSpace Proof of Space // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/proof_of_space.py#L20 -// @TODO Streamable type ProofOfSpace struct { - Challenge Bytes32 `json:"challenge"` - PoolPublicKey mo.Option[G1Element] `json:"pool_public_key"` // Only one of these two should be present - PoolContractPuzzleHash mo.Option[Bytes32] `json:"pool_contract_puzzle_hash"` - PlotPublicKey G1Element `json:"plot_public_key"` - Size uint8 `json:"size"` - Proof Bytes `json:"proof"` + Challenge Bytes32 `json:"challenge" streamable:""` + PoolPublicKey mo.Option[G1Element] `json:"pool_public_key" streamable:""` // Only one of these two should be present + PoolContractPuzzleHash mo.Option[Bytes32] `json:"pool_contract_puzzle_hash" streamable:""` + PlotPublicKey G1Element `json:"plot_public_key" streamable:""` + Size uint8 `json:"size" streamable:""` + Proof Bytes `json:"proof" streamable:""` } diff --git a/pkg/types/slot.go b/pkg/types/slot.go new file mode 100644 index 0000000..722684c --- /dev/null +++ b/pkg/types/slot.go @@ -0,0 +1,42 @@ +package types + +import "github.com/samber/mo" + +// https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/slots.py + +// ChallengeBlockInfo is a type of challenge_block_info +type ChallengeBlockInfo struct { + ProofOfSpace ProofOfSpace `json:"proof_of_space" streamable:""` + ChallengeChainSPVDF mo.Option[VDFInfo] `json:"challenge_chain_sp_vdf" streamable:""` + ChallengeChainSPSignature G2Element `json:"challenge_chain_sp_signature" streamable:""` + ChallengeChainIPVDF VDFInfo `json:"challenge_chain_ip_vdf" streamable:""` +} + +// ChallengeChainSubSlot is a type of challenge_chain_sub_slot +type ChallengeChainSubSlot struct { + ChallengeChainEndOfSlotVDF VDFInfo `json:"challenge_chain_end_of_slot_vdf" streamable:""` + InfusedChallengeChainSubSlotHash mo.Option[Bytes32] `json:"infused_challenge_chain_sub_slot_hash" streamable:""` + SubepochSummaryHash mo.Option[Bytes32] `json:"subepoch_summary_hash" streamable:""` + NewSubSlotIters mo.Option[uint64] `json:"new_sub_slot_iters" streamable:""` + NewDifficulty mo.Option[uint64] `json:"new_difficulty" streamable:""` +} + +// InfusedChallengeChainSubSlot is a type of infused_challenge_chain_sub_slot +type InfusedChallengeChainSubSlot struct { + InfusedChallengeChainEndOfSlotVDF VDFInfo `json:"infused_challenge_chain_end_of_slot_vdf" streamable:""` +} + +// RewardChainSubSlot is a type of reward_chain_sub_slot +type RewardChainSubSlot struct { + EndOfSlotVDF VDFInfo `json:"end_of_slot_vdf" streamable:""` + ChallengeChainSubSlotHash Bytes32 `json:"challenge_chain_sub_slot_hash" streamable:""` + InfusedChallengeChainSubSlotHash mo.Option[Bytes32] `json:"infused_challenge_chain_sub_slot_hash" streamable:""` + Deficit uint8 `json:"deficit" streamable:""` +} + +// SubSlotProofs is a type of sub_slot_proofs +type SubSlotProofs struct { + ChallengeChainSlotProof VDFProof `json:"challenge_chain_slot_proof" streamable:""` + InfusedChallengeChainSlotProof mo.Option[VDFProof] `json:"infused_challenge_chain_slot_proof" streamable:""` + RewardChainSlotProof VDFProof `json:"reward_chain_slot_proof" streamable:""` +} diff --git a/pkg/types/uint128.go b/pkg/types/uint128.go index 0383af3..2f7fd73 100644 --- a/pkg/types/uint128.go +++ b/pkg/types/uint128.go @@ -41,7 +41,8 @@ var Uint128Max = NewUint128(math.MaxUint64, math.MaxUint64) // A Uint128 is an unsigned 128-bit number. type Uint128 struct { - Lo, Hi uint64 + Lo uint64 `streamable:""` + Hi uint64 `streamable:""` } // IsZero returns true if u == 0. diff --git a/pkg/types/vdf.go b/pkg/types/vdf.go index f5706e4..675d2d5 100644 --- a/pkg/types/vdf.go +++ b/pkg/types/vdf.go @@ -2,18 +2,16 @@ package types // VDFInfo VDF Info // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/vdf.py#L49 -// @TODO Streamable type VDFInfo struct { - Challenge Bytes32 `json:"challenge"` - NumberOfIterations uint64 `json:"number_of_iterations"` - Output ClassgroupElement `json:"output"` + Challenge Bytes32 `json:"challenge" streamable:""` + NumberOfIterations uint64 `json:"number_of_iterations" streamable:""` + Output ClassgroupElement `json:"output" streamable:""` } // VDFProof VDF Proof // https://github.com/Chia-Network/chia-blockchain/blob/main/chia/types/blockchain_format/vdf.py#L57 -// @TODO Streamable type VDFProof struct { - WitnessType uint8 `json:"witness_type"` - Witness Bytes `json:"witness"` - NormalizedToIdentity bool `json:"normalized_to_identity"` + WitnessType uint8 `json:"witness_type" streamable:""` + Witness Bytes `json:"witness" streamable:""` + NormalizedToIdentity bool `json:"normalized_to_identity" streamable:""` }