一個C++實現的簡單區塊鏈
原始碼
https://github.com/lzj112/C-Plus-Plus/tree/master/%E8%BD%AF%E4%BB%B6%E6%9D%AF/blockchain/test1
- block.h定義區塊資料結構
#ifndef _BLOCK_H_
#define _BLOCK_H_
#include <iostream>
using namespace std;
struct BlockHead //區塊頭
{
string PreHash; //前一區塊雜湊
//string MrekleTreeRoot; 梅克爾樹根雜湊
string Data; //簡化操作,Data表示資料,本應該是梅克爾樹根雜湊值
string TimeStamp; //時間戳
int Index; //索引
};
class Block : public BlockHead
{
public:
//int size; //區塊大小
//int Separator = 0xD9B4BEF9; //分隔符
string Hash; //本區塊區塊頭的雜湊值
Block() {}
Block(int index, string prehash);
string GetHash(); //計算本區塊雜湊值
void Input(); // 輸入資料
string GetTime(); //得到當前時間戳
};
#endif
- block.cpp
#include "block.h"
#include "md5.h"
#include<time.h>
#include<cstdlib>
#include<cstring>
#include<string>
#include<vector>
Block::Block(int index, string prehash)
{
PreHash = prehash;
Index = index;
TimeStamp = GetTime();
Hash = GetHash();
}
string Block::GetHash()
{
string tmp;
tmp = PreHash + Data + TimeStamp + to_string(Index);
unsigned char* t = (unsigned char*)tmp.data();
MD5 md5;
md5.GenerateMD5(t, tmp.size());
return md5.ToString();
}
void Block::Input()
{
string str;
cout << "輸入資料 : " ;
cin >> str;
Data = str;
}
string Block::GetTime()
{
time_t t;
tm *local;
char buf[256];
t = time( nullptr );
local = localtime( &t );
strftime( buf, 64, "%Y-%m-%d %H:%M:%S",local ); //轉換 buf裡存時間
string time(buf);
return time;
}
- chain.h定義鏈
#ifndef _CHAIN_H_
#define _CHAIN_H_
#include <iostream>
#include "block.h"
#include <string>
#include <vector>
using namespace std;
class Chain
{
public:
vector<Block> BlockChainTmp;
Chain(); //新增創世區塊
~Chain();
void AddBlock(Block t); //新增新區快
void Output(); //遍歷輸出鏈
};
#endif
- chain.cpp
#include "chain.h"
Chain::Chain()
{
Block tmp;
tmp.PreHash = "";
tmp.Data = "";
tmp.Index = 0;
tmp.TimeStamp = tmp.GetTime();
tmp.Hash = tmp.GetHash();
BlockChainTmp.push_back(tmp);
}
Chain::~Chain()
{
}
void Chain::AddBlock(Block t)
{
BlockChainTmp.push_back(t);
}
void Chain::Output()
{
for (auto x : BlockChainTmp)
{
cout << "-------------------------------------------------------------\n"
<< "There is the " << x.Index << " block : \n"
<< "his PreHah : " << x.PreHash << endl
<< "his TimeStamp : " << x.TimeStamp << endl
<< "his Hash : " << x.Hash << endl
<< "his Dta : " << x.Data << endl;
}
}
- md5.h 加密
#ifndef _MD5_H
#define _MD5_H
//#pragma warning(disable:4786)
#include <string>
using namespace std;
/*!
* Manage MD5.
*/
class MD5
{
private:
#define uint8 unsigned char
#define uint32 unsigned long int
struct md5_context
{
uint32 total[2];
uint32 state[4];
uint8 buffer[64];
};
void md5_starts( struct md5_context *ctx );
void md5_process( struct md5_context *ctx, uint8 data[64] );
void md5_update( struct md5_context *ctx, uint8 *input, uint32 length );
void md5_finish( struct md5_context *ctx, uint8 digest[16] );
public:
//! construct a MD5 from any buffer
void GenerateMD5(unsigned char* buffer,int bufferlen);
//! construct a MD5
MD5();
//! construct a md5src from char *
MD5(const char * md5src);
//! construct a MD5 from a 16 bytes md5
MD5(unsigned long* md5src);
//! add a other md5
MD5 operator +(MD5 adder);
//! just if equal
bool operator ==(MD5 cmper);
//! give the value from equer
// void operator =(MD5 equer);
//! to a string
string ToString();
unsigned long m_data[4];
};
#endif
- md5.cpp
#include "md5.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define GET_UINT32(n,b,i) \
{ \
(n) = (uint32) ((uint8 *) b)[(i)] \
| (((uint32) ((uint8 *) b)[(i)+1]) << 8) \
| (((uint32) ((uint8 *) b)[(i)+2]) << 16) \
| (((uint32) ((uint8 *) b)[(i)+3]) << 24); \
}
#define PUT_UINT32(n,b,i) \
{ \
(((uint8 *) b)[(i)] ) = (uint8) (((n) ) & 0xFF); \
(((uint8 *) b)[(i)+1]) = (uint8) (((n) >> 8) & 0xFF); \
(((uint8 *) b)[(i)+2]) = (uint8) (((n) >> 16) & 0xFF); \
(((uint8 *) b)[(i)+3]) = (uint8) (((n) >> 24) & 0xFF); \
}
//extern pthread_mutex_t mutexMemory;
void MD5::md5_starts( struct md5_context *ctx )
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
}
void MD5::md5_process( struct md5_context *ctx, uint8 data[64] )
{
uint32 A, B, C, D, X[16];
GET_UINT32( X[0], data, 0 );
GET_UINT32( X[1], data, 4 );
GET_UINT32( X[2], data, 8 );
GET_UINT32( X[3], data, 12 );
GET_UINT32( X[4], data, 16 );
GET_UINT32( X[5], data, 20 );
GET_UINT32( X[6], data, 24 );
GET_UINT32( X[7], data, 28 );
GET_UINT32( X[8], data, 32 );
GET_UINT32( X[9], data, 36 );
GET_UINT32( X[10], data, 40 );
GET_UINT32( X[11], data, 44 );
GET_UINT32( X[12], data, 48 );
GET_UINT32( X[13], data, 52 );
GET_UINT32( X[14], data, 56 );
GET_UINT32( X[15], data, 60 );
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a,b,c,d,k,s,t) \
{ \
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x,y,z) (z ^ (x & (y ^ z)))
P( A, B, C, D, 0, 7, 0xD76AA478 );
P( D, A, B, C, 1, 12, 0xE8C7B756 );
P( C, D, A, B, 2, 17, 0x242070DB );
P( B, C, D, A, 3, 22, 0xC1BDCEEE );
P( A, B, C, D, 4, 7, 0xF57C0FAF );
P( D, A, B, C, 5, 12, 0x4787C62A );
P( C, D, A, B, 6, 17, 0xA8304613 );
P( B, C, D, A, 7, 22, 0xFD469501 );
P( A, B, C, D, 8, 7, 0x698098D8 );
P( D, A, B, C, 9, 12, 0x8B44F7AF );
P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
P( B, C, D, A, 11, 22, 0x895CD7BE );
P( A, B, C, D, 12, 7, 0x6B901122 );
P( D, A, B, C, 13, 12, 0xFD987193 );
P( C, D, A, B, 14, 17, 0xA679438E );
P( B, C, D, A, 15, 22, 0x49B40821 );
#undef F
#define F(x,y,z) (y ^ (z & (x ^ y)))
P( A, B, C, D, 1, 5, 0xF61E2562 );
P( D, A, B, C, 6, 9, 0xC040B340 );
P( C, D, A, B, 11, 14, 0x265E5A51 );
P( B, C, D, A, 0, 20, 0xE9B6C7AA );
P( A, B, C, D, 5, 5, 0xD62F105D );
P( D, A, B, C, 10, 9, 0x02441453 );
P( C, D, A, B, 15, 14, 0xD8A1E681 );
P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
P( A, B, C, D, 9, 5, 0x21E1CDE6 );
P( D, A, B, C, 14, 9, 0xC33707D6 );
P( C, D, A, B, 3, 14, 0xF4D50D87 );
P( B, C, D, A, 8, 20, 0x455A14ED );
P( A, B, C, D, 13, 5, 0xA9E3E905 );
P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
P( C, D, A, B, 7, 14, 0x676F02D9 );
P( B, C, D, A, 12, 20, 0x8D2A4C8A );
#undef F
#define F(x,y,z) (x ^ y ^ z)
P( A, B, C, D, 5, 4, 0xFFFA3942 );
P( D, A, B, C, 8, 11, 0x8771F681 );
P( C, D, A, B, 11, 16, 0x6D9D6122 );
P( B, C, D, A, 14, 23, 0xFDE5380C );
P( A, B, C, D, 1, 4, 0xA4BEEA44 );
P( D, A, B, C, 4, 11, 0x4BDECFA9 );
P( C, D, A, B, 7, 16, 0xF6BB4B60 );
P( B, C, D, A, 10, 23, 0xBEBFBC70 );
P( A, B, C, D, 13, 4, 0x289B7EC6 );
P( D, A, B, C, 0, 11, 0xEAA127FA );
P( C, D, A, B, 3, 16, 0xD4EF3085 );
P( B, C, D, A, 6, 23, 0x04881D05 );
P( A, B, C, D, 9, 4, 0xD9D4D039 );
P( D, A, B, C, 12, 11, 0xE6DB99E5 );
P( C, D, A, B, 15, 16, 0x1FA27CF8 );
P( B, C, D, A, 2, 23, 0xC4AC5665 );
#undef F
#define F(x,y,z) (y ^ (x | ~z))
P( A, B, C, D, 0, 6, 0xF4292244 );
P( D, A, B, C, 7, 10, 0x432AFF97 );
P( C, D, A, B, 14, 15, 0xAB9423A7 );
P( B, C, D, A, 5, 21, 0xFC93A039 );
P( A, B, C, D, 12, 6, 0x655B59C3 );
P( D, A, B, C, 3, 10, 0x8F0CCC92 );
P( C, D, A, B, 10, 15, 0xFFEFF47D );
P( B, C, D, A, 1, 21, 0x85845DD1 );
P( A, B, C, D, 8, 6, 0x6FA87E4F );
P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
P( C, D, A, B, 6, 15, 0xA3014314 );
P( B, C, D, A, 13, 21, 0x4E0811A1 );
P( A, B, C, D, 4, 6, 0xF7537E82 );
P( D, A, B, C, 11, 10, 0xBD3AF235 );
P( C, D, A, B, 2, 15, 0x2AD7D2BB );
P( B, C, D, A, 9, 21, 0xEB86D391 );
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
}
void MD5::md5_update( struct md5_context *ctx, uint8 *input, uint32 length )
{
uint32 left, fill;
if( ! length ) return;
left = ( ctx->total[0] >> 3 ) & 0x3F;
fill = 64 - left;
ctx->total[0] += length << 3;
ctx->total[1] += length >> 29;
ctx->total[0] &= 0xFFFFFFFF;
ctx->total[1] += ctx->total[0] < length << 3;
if( left && length >= fill )
{
memcpy( (void *) (ctx->buffer + left), (void *) input, fill );
md5_process( ctx, ctx->buffer );
length -= fill;
input += fill;
left = 0;
}
while( length >= 64 )
{
md5_process( ctx, input );
length -= 64;
input += 64;
}
if( length )
{
memcpy( (void *) (ctx->buffer + left), (void *) input, length );
}
}
static uint8 md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void MD5::md5_finish( struct md5_context *ctx, uint8 digest[16] )
{
uint32 last, padn;
uint8 msglen[8];
PUT_UINT32( ctx->total[0], msglen, 0 );
PUT_UINT32( ctx->total[1], msglen, 4 );
last = ( ctx->total[0] >> 3 ) & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
md5_update( ctx, md5_padding, padn );
md5_update( ctx, msglen, 8 );
PUT_UINT32( ctx->state[0], digest, 0 );
PUT_UINT32( ctx->state[1], digest, 4 );
PUT_UINT32( ctx->state[2], digest, 8 );
PUT_UINT32( ctx->state[3], digest, 12 );
}
void MD5::GenerateMD5(unsigned char* buffer,int bufferlen)
{
struct md5_context context;
md5_starts (&context);
md5_update (&context, buffer, bufferlen);
md5_finish (&context,(unsigned char*)m_data);
}
MD5::MD5()
{
for(int i=0;i<4;i++)
m_data[i]=0;
}
MD5::MD5(unsigned long* md5src)
{
memcpy(m_data,md5src,16);
}
int _httoi(const char *value)
{
struct CHexMap
{
char chr;
int value;
};
const int HexMapL = 16;
CHexMap HexMap[HexMapL] =
{
{'0', 0}, {'1', 1},
{'2', 2}, {'3', 3},
{'4', 4}, {'5', 5},
{'6', 6}, {'7', 7},
{'8', 8}, {'9', 9},
{'a', 10}, {'b', 11},
{'c', 12}, {'d', 13},
{'e', 14}, {'f', 15}
};
//pthread_mutex_lock(&mutexMemory);
char *mstr = strdup(value);
//pthread_mutex_unlock(&mutexMemory);
char *s = mstr;
int result = 0;
if (*s == '0' && *(s + 1) == 'X') s += 2;
bool firsttime = true;
while (*s != '/0')
{
bool found = false;
for (int i = 0; i < HexMapL; i++)
{
if (*s == HexMap[i].chr)
{
if (!firsttime) result <<= 4;
result |= HexMap[i].value;
found = true;
break;
}
}
if (!found) break;
s++;
firsttime = false;
}
//pthread_mutex_lock(&mutexMemory);
free(mstr);
//pthread_mutex_unlock(&mutexMemory);
return result;
}
MD5::MD5(const char* md5src)
{
if (strcmp(md5src,"")==0)
{
for(int i=0;i<4;i++)
m_data[i]=0;
return;
}
for(int j = 0; j < 16; j++ )
{
char buf[10];
strncpy(buf,md5src,2);
md5src+=2;
((unsigned char*)m_data)[j] = _httoi(buf);
}
}
MD5 MD5::operator +(MD5 adder)
{
unsigned long m_newdata[4];
for(int i=0;i<4;i++)
m_newdata[i]=m_data[i]^(adder.m_data[i]);
return MD5(m_newdata);
}
bool MD5::operator ==(MD5 cmper)
{
return (memcmp(cmper.m_data ,m_data,16)==0);
}
//void MD5::operator =(MD5 equer)
//{
// memcpy(m_data,equer.m_data ,16);
//}
string MD5::ToString()
{
char output[33];
for(int j = 0; j < 16; j++ )
{
sprintf( output + j * 2, "%02x", ((unsigned char*)m_data)[j]);
}
return string(output);
}
- main.cpp
#include <iostream>
#include <vector>
#include "block.h"
#include "chain.h"
#include "md5.h"
using namespace std;
int main()
{
Chain BlockChain;
int index;
int flag = 5;
string str, prehash;
while (flag--) //EOF結束
{
auto it = BlockChain.BlockChainTmp.end();
index = it[-1].Index + 1;
prehash = it[-1].Hash;
Block t(index, prehash);
t.Input();
BlockChain.AddBlock(t);
}
BlockChain.Output();
}
輸出結果
輸入資料 : a
輸入資料 : b
輸入資料 : c
輸入資料 : d
輸入資料 : e
-------------------------------------------------------------
There is the 0 block :
his PreHah :
his TimeStamp : 2018-05-18 17:53:04
his Hash : 7ceaccf4b8b6055df453311c8f27c3e2
his Dta :
-------------------------------------------------------------
There is the 1 block :
his PreHah : 7ceaccf4b8b6055df453311c8f27c3e2
his TimeStamp : 2018-05-18 17:53:04
his Hash : dd81d72186b5512597a1bca58ba8faf5
his Dta : a
-------------------------------------------------------------
There is the 2 block :
his PreHah : dd81d72186b5512597a1bca58ba8faf5
his TimeStamp : 2018-05-18 17:53:05
his Hash : 7c95afa850c847c0006dac98b7c74878
his Dta : b
-------------------------------------------------------------
There is the 3 block :
his PreHah : 7c95afa850c847c0006dac98b7c74878
his TimeStamp : 2018-05-18 17:53:06
his Hash : 0937670f07b677c9536f52f67366067c
his Dta : c
-------------------------------------------------------------
There is the 4 block :
his PreHah : 0937670f07b677c9536f52f67366067c
his TimeStamp : 2018-05-18 17:53:06
his Hash : 4d41bc930aeb39d90853e9ee603a9009
his Dta : d
-------------------------------------------------------------
There is the 5 block :
his PreHah : 4d41bc930aeb39d90853e9ee603a9009
his TimeStamp : 2018-05-18 17:53:07
his Hash : 9eafa625356af2717a487ba22b9ef563
his Dta : e
相關推薦
一個C++實現的簡單區塊鏈
原始碼 https://github.com/lzj112/C-Plus-Plus/tree/master/%E8%BD%AF%E4%BB%B6%E6%9D%AF/blockchain/test1 block.h定義區塊資料結構 #ifndef
Golang實現區塊鏈(一)—簡單區塊鏈
隨著比特幣、以太坊等虛擬貨幣的越來越火,作為這些虛擬貨幣背後支撐的區塊鏈技術,也被越來越多的人提及。下面我們將使用go語言對區塊鏈進行探討,並實現一個簡易的區塊鏈,本文暫不涉及poW、poS等共識演算法。 通過本文,你可以做到: 瞭解區塊 Hash演算法
簡單區塊鏈的實現(帶POW挖礦系統)
前言 在IT界,2018最火的熱詞相必就是區塊鏈了,C++和GO是目前最適合區塊鏈開發的兩種語言,所以咱們學GO的肯定得學一點區塊鏈的知識,但是區塊鏈涉及太多密碼學,金融學、p2p網路等知識了,從哪裡切入呢,今天我們就從用go實現一條帶有模擬挖礦系統的簡單區塊
C++實現簡單的文本查詢
ber number map () first begin ifstream adf times 1 該程序將讀取用戶指定的任意文本文件,然後允許用戶從該文件中查找單詞。查詢的結果是該單詞出現的次數,並列出每次出現所在的行。如果某單詞在同一行中多次出現,程序將只顯示該
c 最簡單的鏈表
node tab while main cnblogs 變量 簡單的 clu str #include <stdio.h> struct node { int data; struct node *next; //指向本身的指針 }; // main() {
C#實現簡單的Http請求實例
semaphore bsp .text block 通過 renren www 字節 req 本文實例講述了C#實現簡單的Http請求的方法。分享給大家供大家參考。具體分析如下: 通過.Net中的兩個類HttpWebRequest類,HttpWebResponse類來實現
C#實現簡單獲取及設置Session類
static 相互 如何 相互轉換 per body share line window 本文實例講述了C#實現簡單獲取及設置Session類。分享給大家供大家參考。具體分析如下: 這是一個簡單的C#獲取Session、設置Session類文件,本類主要實現大家最常用的兩
C#實現簡單的冒泡排序
read pro i++ cto bsp con nbsp ces nag 1、C#代碼下:using System;namespace ConsoleApplication1{ class Program { static void Main()
C++實現查找鏈表中環的入口節點
n) head break 控制 style fast ast 無限循環 AC /* * 尋找鏈表中環的入口節點.cpp * * Created on: 2018年4月10日 * Author: soyo */ #include<iostrea
c實現 簡單的文件管理 不含交互
字符 當前 str2 masm 器) 上層 stream file style 實現如下功能: 1、讀取指定目錄下的所有子目錄和文件信息(比如:指定目錄為C:/temp則把此目錄 下的所有子目錄下的文件信息讀出來)2、在C盤創建一個以個人姓名命名的目錄(比如:張三)3、在
C++ 實現簡單命令行學生管理系統
什麽 cos wid 屏幕 cit 環境 iterator choice umeng C++ 實現簡單命令行學生管理系統 貼吧ID: 這把問題不大 編譯環境是macOS。system(“clear”) 在windows下請換成 system(“cls”) #include
用 Python 構建一個極小的區塊鏈
計算機 特定 使用 lock 為我 並且 沒有 python學習 為什麽 雖然有些人認為區塊鏈是一個早晚會出現問題的解決方案,但是毫無疑問,這個創新技術是一個計算機技術上的奇跡。那麽,究竟什麽是區塊鏈呢? 區塊鏈 以比特幣(Bitcoin)或其它加密
啟發!一個小白的區塊鏈自學歷程
2018年初到現在,很多人看著區塊鏈的變化,斷言區塊鏈就是泡沫,我想很多人都會持這樣的觀點,但實情果真如此嗎? 如果你有了解過這門技術,有對現今的行業和長遠的見識,你就不難發現,其實區塊鏈涉及多個技術的整合,其中包括密碼學、計算機網路、博弈論、系統工程等複雜度頗高的學科,
Python 模擬簡單區塊鏈
首先這是說明一下這是Tiny熊老師的教程https://www.cnblogs.com/tinyxiong 另外還要說明一下,暑假指導老師讓我們做一些關於區塊鏈的應用。這裡只是涉及極其簡單的模擬,主要是記錄這些天自己學習的知識。 什麼是區塊鏈? 下面簡單說一下區塊鏈是什麼,做個比喻,區塊就像一個人,區塊
c++實現簡單的string類
學得快,忘得快,老師講完了運算子過載這一節然後就帶我們開發一個字串類,我的程式碼也是在理解的基礎上跟著老師敲的,覺得太有意思了!!!趕緊寫部落格總結一下,加深理解,希望我這記性別讓我失望!其實字串也是可以理解為字元陣列,所以在類中有兩個屬性,字串長度len和字元指標,當定義mystring
用C++實現簡單的檔案I/O操作
檔案 I/O 在C++中比烤蛋糕簡單多了。 在這篇文章裡,我會詳細解釋ASCII和二進位制檔案的輸入輸出的每個細節,值得注意的是,所有這些都是用C++完成的。 一、ASCII 輸出 為了使用下面的方法, 你必須包含標頭檔案<fstream.h>(譯者注:在標準C++中,已
c++實現簡單的Http客戶端協議,WebRequest
最近要寫一個代理程式,軟體最終要跑在嵌入式裝置上,其中一部分是需要做一個簡單爬蟲程式,用來操作嵌入式裝置的Web服務上的資訊,我不想用第三方的任何庫,如是簡單看了下http協議,用一天時間實現了http協議的客戶端,實現Get,Post,UpFile(檔案上傳)等
跨境市場下一個藍海:區塊鏈+跨境支付?
全球經濟的現在需要跨境支付的場景越來越多,比如出國旅遊,求學,海外購物等,但是跨境支付中會面臨高昂手續費,交易過程繁瑣,收款時間漫長等問題。 跨境市場:下一個藍海 隨著近年來跨境電商的迅猛發展,越來越多的優質海外商品鄭加速進入中國市場,跨境市場也將成為下一個萬億級別的“藍海”。 目前
C#實現簡單的學生管理系統增刪改查
1.Programs.cs using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Syste
用c#實現簡單文字編譯器
上學期有個程式設計實驗,其中一個是需要實現一個帶介面的簡單文字編輯器。這裡選擇用c#來實現。 頁面設計和具體功能 在檔案處選擇新建一個文字,這裡的文字使用窗體實現載入 有一些快捷鍵:比如字型選擇按鈕,點選以後可以出現一個窗體,實現字型的選擇。但是這裡貌似有點