Built into the Blockcore foundation there are some payloads used to handle the peer to peer protocol and connections of nodes, and exchange of blockchain data.
Here are the built in primary payload types with their command names in parentheses:
AddrPayload ("addr")
BlockPayload ("block")
GetAddrPayload ("getaddr")
GetBlocksPayload ("getblocks")
GetDataPayload ("getdata")
GetHeadersPayload ("getheaders")
GetProvenHeadersPayload ("getprovhdr")
HaveWitnessPayload ("havewitness")
HeadersPayload ("headers")
InvPayload ("inv")
MempoolPayload ("mempool")
NotFoundPayload
PingPayload ("ping")
PongPayload ("pong")
ProvenHeadersPayload ("provhdr")
RejectPayload ("reject")
SendHeadersPayload ("sendheaders")
TxPayload ("tx")
UnknowPayload
VerAckPayload ("verack")
VersionPayload ("version")
Here are payload types defined in features:
FeeFilterPayload ("feefilter")
PoAHeaderPayload ("poahdr")
StoragePayload ("storage")
StorageInvPayload ("storageinv")
Here is a code map of the payload in the Blockcore assembly:
Here is a code map of the custom payload types defined in features:
Your own custom payload
You can implement your own custom payloads, which is a great way to build a communication solution between nodes connected in a peer to peer fashion.
You can use these custom features to build chat solutions, data transfer solutions or any other logic that can be useful for a network of nodes.
All you need is one or multiple payload types and an behavior class.
Example of a payload. You should define these classes inside your own custom feature.
// This sample is copied from the "ping" payload.usingNBitcoin;namespaceBlockcore.P2P.Protocol.Payloads{[Payload("storage")]publicclassStoragePayload:Payload{privateuint[]items;publicuint[]Items{get{returnthis.items;}set{this.items=value;}}publicStoragePayload(uint[]items){this.items=items;}publicoverridevoidReadWriteCore(BitcoinStreamstream){stream.ReadWrite(refthis.items);}publicoverridestringToString(){returnbase.ToString()+" : "+this.Items;}}}
Then you also need a behavior that will process messages.
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Threading.Tasks;usingBlockcore.Connection;usingBlockcore.Interfaces;usingBlockcore.P2P.Peer;usingBlockcore.P2P.Protocol;usingBlockcore.P2P.Protocol.Behaviors;usingBlockcore.P2P.Protocol.Payloads;usingBlockcore.Signals;usingBlockcore.Utilities;usingMicrosoft.Extensions.Logging;usingNBitcoin;namespaceBlockcore.Features.Storage{/// <summary>/// Peer behavior for memory pool./// Provides message handling of notifications from attached peer./// </summary>publicclassStorageBehavior:NetworkPeerBehavior{/// <summary>Connection manager for managing peer connections.</summary>privatereadonlyIConnectionManagerconnectionManager;/// <summary>Provider of IBD state.</summary>privatereadonlyIInitialBlockDownloadStateinitialBlockDownloadState;/// <summary>Instance logger for the memory pool behavior component.</summary>privatereadonlyILoggerlogger;/// <summary>Factory used to create the logger for this component.</summary>privatereadonlyILoggerFactoryloggerFactory;/// <summary>The network that this component is running on.</summary>privatereadonlyNetworknetwork;/// <summary>/// Locking object for memory pool behaviour./// </summary>privatereadonlyobjectlockObject;/// <summary>/// The min fee the peer asks to relay transactions./// </summary>publicMoneyMinFeeFilter{get;set;}publicStorageBehavior(IConnectionManagerconnectionManager,IInitialBlockDownloadStateinitialBlockDownloadState,ILoggerFactoryloggerFactory,Networknetwork){this.connectionManager=connectionManager;this.initialBlockDownloadState=initialBlockDownloadState;this.logger=loggerFactory.CreateLogger(this.GetType().FullName);this.loggerFactory=loggerFactory;this.network=network;this.lockObject=newobject();}/// <inheritdoc />protectedoverridevoidAttachCore(){this.AttachedPeer.MessageReceived.Register(this.OnMessageReceivedAsync);}/// <inheritdoc />protectedoverridevoidDetachCore(){this.AttachedPeer.MessageReceived.Unregister(this.OnMessageReceivedAsync);}/// <inheritdoc />publicoverrideobjectClone(){returnnewStorageBehavior(this.connectionManager,this.initialBlockDownloadState,this.loggerFactory,this.network);}privateasyncTaskOnMessageReceivedAsync(INetworkPeerpeer,IncomingMessagemessage){try{awaitthis.ProcessMessageAsync(peer,message).ConfigureAwait(false);}catch(OperationCanceledException){this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");return;}catch(Exceptionex){this.logger.LogError("Exception occurred: {0}",ex.ToString());throw;}}privateasyncTaskProcessMessageAsync(INetworkPeerpeer,IncomingMessagemessage){try{switch(message.Message.Payload){caseStoragePayloadstoragePayload:awaitthis.ProcessStorageInPayloadAsync(peer,storagePayload).ConfigureAwait(false);break;}}catch(OperationCanceledException){this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");return;}}privateasyncTaskProcessStorageInPayloadAsync(INetworkPeerpeer,StoragePayloadmessage){Guard.NotNull(peer,nameof(peer));if(peer!=this.AttachedPeer){this.logger.LogDebug("Attached peer '{0}' does not match the originating peer '{1}'.",this.AttachedPeer?.RemoteSocketEndpoint,peer.RemoteSocketEndpoint);this.logger.LogTrace("(-)[PEER_MISMATCH]");return;}List<uint>list=newList<uint>();list.Add(1);list.Add(2);list.Add(3);awaitthis.SendStorageAsync(peer,list);}privateasyncTaskSendStorageAsync(INetworkPeerpeer,List<uint>storageList){varqueue=newQueue<uint>(storageList);while(queue.Count>0){uint[]items=queue.TakeAndRemove(5).ToArray();if(peer.IsConnected){this.logger.LogDebug("Sending items to peer '{0}'.",peer.RemoteSocketEndpoint);awaitpeer.SendMessageAsync(newStoragePayload(items)).ConfigureAwait(false);}}}publicasyncTaskSendTrickleAsync(){INetworkPeerpeer=this.AttachedPeer;if(peer==null){this.logger.LogTrace("(-)[NO_PEER]");return;}this.logger.LogDebug("Sending storage inventory to peer '{0}'.",peer.RemoteSocketEndpoint);try{// Sample data to send.List<uint>list=newList<uint>();list.Add(1);list.Add(2);awaitthis.SendStorageAsync(peer,list).ConfigureAwait(false);}catch(OperationCanceledException){this.logger.LogTrace("(-)[CANCELED_EXCEPTION]");return;}}}}
usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Threading.Tasks;usingBlockcore.AsyncWork;usingBlockcore.Builder;usingBlockcore.Builder.Feature;usingBlockcore.Configuration.Logging;usingBlockcore.Connection;usingBlockcore.Features.Storage.Persistence;usingBlockcore.P2P.Peer;usingBlockcore.P2P.Protocol.Payloads;usingBlockcore.Utilities;usingMicrosoft.Extensions.DependencyInjection;namespaceBlockcore.Features.Storage{publicclassStorageFeature:FullNodeFeature{/// <summary>The async loop we need to wait upon before we can shut down this manager.</summary>privateIAsyncLoopasyncLoop;/// <summary>Factory for creating background async loop tasks.</summary>privatereadonlyIAsyncProviderasyncProvider;/// <summary>/// Connection manager injected dependency./// </summary>privatereadonlyIConnectionManagerconnection;/// <summary>Global application life cycle control - triggers when application shuts down.</summary>privatereadonlyINodeLifetimenodeLifetime;privatereadonlyPayloadProviderpayloadProvider;privatereadonlyStorageBehaviorstorageBehavior;publicStorageFeature(IConnectionManagerconnection,INodeLifetimenodeLifetime,IAsyncProviderasyncProvider,PayloadProviderpayloadProvider,StorageBehaviorstorageBehavior){this.connection=connection;this.nodeLifetime=nodeLifetime;this.payloadProvider=payloadProvider;this.asyncProvider=asyncProvider;this.storageBehavior=storageBehavior;}publicoverrideTaskInitializeAsync(){// Register the behavior.this.connection.Parameters.TemplateBehaviors.Add(this.storageBehavior);// Register the payload types.this.payloadProvider.AddPayload(typeof(StoragePayload));// Make a worker that will filter connected knows that has announced our custom payload behavior.this.asyncLoop=this.asyncProvider.CreateAndRunAsyncLoop("Storage.SyncWorker",asynctoken=>{IReadOnlyNetworkPeerCollectionpeers=this.connection.ConnectedPeers;if(!peers.Any()){return;}// Announce the blocks on each nodes behavior which supports relaying.IEnumerable<StorageBehavior>behaviors=peers.Where(x=>x.PeerVersion?.Relay??false).Select(x=>x.Behavior<StorageBehavior>()).Where(x=>x!=null).ToList();foreach(StorageBehaviorbehaviorinbehaviors){awaitbehavior.SendTrickleAsync().ConfigureAwait(false);}},this.nodeLifetime.ApplicationStopping,repeatEvery:TimeSpans.FiveSeconds,startAfter:TimeSpans.TenSeconds);returnTask.CompletedTask;}}/// <summary>/// A class providing extension methods for <see cref="IFullNodeBuilder"/>./// </summary>publicstaticclassFullNodeBuilderStorageExtension{publicstaticIFullNodeBuilderUseStorage(thisIFullNodeBuilderfullNodeBuilder){LoggingConfiguration.RegisterFeatureNamespace<StorageFeature>("storage");fullNodeBuilder.ConfigureFeature(features=>{features.AddFeature<StorageFeature>().FeatureServices(services=>{services.AddSingleton<IDataStore,DataStore>();services.AddSingleton<StorageBehavior>();});});returnfullNodeBuilder;}}}
This is basically all you need to start building custom data payload features ontop of your network of Blockcore nodes.