EIP 155:重放攻击保护——防止了在一个以太坊链上的交易被重复广播到另外一条链。

在看椭圆曲线时有提到,与r、s、v中的v相关

不同的共有链定义不同的chainId, 防止同一笔交易在不同的共有链上进行两次交易,防止重放攻击(其实就是防止测试网中的代币发送到主网中去)

v = 35 + chainId *2 | v = 36 + chainId * 2 (奇偶校验)

在创世区块的json文件中有设置:

    "config": {
"chainId": ,
"homesteadBlock": ,
"eip155Block": ,//homesteadeip155两种版本都不是,那么就是Frontier版本了,所以v的值为27或28,看下面代码解释
"eip158Block":
},

参数代表的含义如下所示:

1、homesteadBlock:代表以太坊版本,这里我们设置为0。

2、eip155Block:我们的区块链并不涉及EIP155硬分叉(hard-fork),因此这个值设置为0。(这就是这个EIP将的内容)

3、eip158Block:同理,我们的区块链并不涉及EIP158硬分叉(hard-fork),因此这个值设置为0。

4.正在公开使用的chainId:

: Olympic, Ethereum public pre-release testnet
: Frontier, Homestead, Metropolis, the Ethereum public main network
: Classic, the (un)forked public Ethereum Classic main network, chain ID
: Expanse, an alternative Ethereum implementation, chain ID
: Morden, the public Ethereum testnet, now Ethereum Classic testnet
: Ropsten, the public cross-client Ethereum testnet
: Rinkeby, the public Geth PoA testnet
: Ubiq, the public Gubiq main network with flux difficulty chain ID
: Kovan, the public Parity PoA testnet
: Sokol, the public POA Network testnet
: Core, the public POA Network main network
: Musicoin, the music blockchain
: Aquachain, ASIC resistant chain
[Other]: Could indicate that your connected to a local development test network.

注意:这里Classic是当年硬分叉后的ETC(叫以太经典),其的chainId为61(DAO(Decentralized Autonomous Organization)去中心化的自治组织);

2,3,4都为测试网络

EIP-155是后向兼容的,向后兼容是指旧软件所产生的数据或者代码可以被新软件使用,即win13能够使用win10的应用

反之,向前兼容即win10能使用win13的应用

Be aware that this backwards compatibility also means that transactions created from alternative Ethereum based blockchains that have not implemented EIP 155 (such as Ethereum Classic) can still be replayed on the main Ethereum chain.

eip title author type category status created
155
Simple replay attack protection
Vitalik Buterin
Standards Track
Core
Final
2016-10-14

Hard fork

Spurious Dragon

Parameters

  • FORK_BLKNUM: 2,675,000 (the DAO 事件后进行分叉的区块数)
  • CHAIN_ID: 1 (main net)

Specification

If block.number >= FORK_BLKNUM and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then when computing the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six elements (i.e. nonce, gasprice, startgas, to, value, data), hash nine elements, with v replaced by CHAIN_IDr = 0 and s = 0. The currently existing signature scheme using v = 27 and v = 28 remains valid and continues to operate under the same rules as it does now.

如果区块数大于分叉区块数并且v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36,那么当为了签名或恢复进行交易hash的计算时,不再仅仅只hash前六个元素,而是(nonce, gasprice, startgas, to, value, data),而是hash九个元素(还要再加上CHAIN_ID,r=0,s=0)。目前存在的使用 v = 27 and v = 28 来保证有效的签名方案将在现在使用的相同规则下继续操作(意思是这个也还在使用)。v = 27 and v = 28说明的是版本(当其在签名中值为0x00或0x01时,要反向得到加密账户,即调用ecrecover函数时,要加27)

Example举例说明

Consider a transaction with nonce = 9gasprice = 20 * 10**9startgas = 21000to = 0x3535353535353535353535353535353535353535value = 10**18data='' (empty).

The "signing data" becomes:

0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080

The "signing hash" becomes:

0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53

If the transaction is signed with the private key 0x4646464646464646464646464646464646464646464646464646464646464646, then the v,r,s values become:

(37, 18515461264373351373200002665853028612451056578545711640558177340181847433846, 46948507304638947509940763649030358759909902576025900602547168820602576006531)

Notice the use of 37 instead of 27. The signed tx would become:

0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83

Rationale

This would provide a way to send transactions that work on Ethereum without working on ETC or the Morden testnet. ETC is encouraged to adopt this EIP but replacing CHAIN_ID with a different value, and all future testnets, consortium chains and alt-etherea are encouraged to adopt this EIP replacing CHAIN_ID with a unique value.

这样子就提供了一种方法来保证交易是使用在了以太坊上而不是另一个分叉ETC或测试网络Morden上,主网上的v = 1*2 + 35 = 37 或 v = 1*2 + 36 = 38(注意:这也是为什么之前在查看一些现在生成的签名的v的时候,发现他们的v并不等于27或28,这个后面要注意一下,为什么还是有些是27和28,这是看你使用的版本的原因,下面有解释)

List of Chain ID's:

CHAIN_ID Chain(s)
1 Ethereum mainnet
2 Morden (disused), Expanse mainnet
3 Ropsten
4 Rinkeby
30 Rootstock mainnet
31 Rootstock testnet
42 Kovan
61 Ethereum Classic mainnet
62 Ethereum Classic testnet
1337 Geth private chains (default)

签名只会生成r,s两个的值,v是加上去的

举个例子说明:

web3.eth.signTransaction({
from: "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0",
gasPrice: "",
gas: "",
to: '0x3535353535353535353535353535353535353535',
value: "",
data: ""
}).then(console.log);
> {
raw: '0xf86c808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a04f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88da07e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0',
tx: {
nonce: '0x0',
gasPrice: '0x4a817c800',
gas: '0x5208',
to: '0x3535353535353535353535353535353535353535',
value: '0xde0b6b3a7640000',
input: '0x',
v: '0x25',
r: '0x4f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88d',
s: '0x7e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0',
hash: '0xda3be87732110de6c1354c83770aae630ede9ac308d9f7b399ecfba23d923384'
}
}

将raw处的签名分解为tx处的内容:

0xf86c8085  4a817c800      de0b6b3a7640000   a0 4f4c17305743700648bc4f6cd3038ec6f6af0df73e31757007b7f59df7bee88d a0 7e1941b264348e80c78c4027afc65a87b0a5e43e86742b8ca0823584c6788fd0

将这里的数据分开后,可以看见每一个数据所在的位置,但是很奇怪的地方就是为什么每个数据之间都有一些奇怪的数字,比如后面的v,r,s,中间都有着0xa0,这些值是有什么意义的吗,希望能找到解释

从这里的结果我们就能够看见这里的v = 0x25 = 37,说明它是在主网运行的

还有另一个例子:

    var rawTx = {
from: '0x91b678137f09c8b4f294a14e88c09276522618cf',
nonce: '0x'+nonce,
gasPrice: '0x09184e72a000',
gasLimit: ,//2dc6c0
to: '0xceff99a34d9f6e7d3deae2bd0604086645368aee',
value: '0x00',
data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057'
};

得到的签名分解是结果是:

f88a0b86  9184e72a000  2dc6c0  ceff99a34d9f6e7d3deae2bd0604086645368aee 80a4 7f7465737432000000000000000000000000000000000000000000000000000000600057 1b a0 b9c52b010ef238339f710bee60b8597b2953c19d32680490b8f0acc82f2fe3be a0 40f8e42202fdb3b72e2f2976fc5e4ae7485b45ca702b23792b38add2d4912ccb

可见这里的v = 0x1b = 27,因为这个结果是我在本地区块链运行的交易,并不涉及EIP155硬分叉(hard-fork),eip155Block这个值是设置为0的

下面由代码上查看:

https://github.com/ethereum/go-ethereum/blob/master/core/types/transaction_signing.go

从代码上我们可以看见,当交易实现的规则不同时,v的值也会相应不同

1.EIP155Signer,则 v = chainId *2 +35,说明使用的是奇偶校验中的偶校验,即曲线点为偶数(如果用的是36,则是奇校验)

2.HomesteadSigner,v is 0 or 1 (0为偶检验,1为奇校验)

3.FrontierSigner,为27 or 28 (27为偶检验,28为奇校验)

go-ethereum/core/types/transaction_signing.go

// EIP155Transaction implements Signer using the EIP155 rules.
type EIP155Signer struct {
chainId, chainIdMul *big.Int
} func NewEIP155Signer(chainId *big.Int) EIP155Signer {
if chainId == nil {
chainId = new(big.Int)
}
return EIP155Signer{
chainId: chainId,
chainIdMul: new(big.Int).Mul(chainId, big.NewInt()),
}
} func (s EIP155Signer) Equal(s2 Signer) bool {
eip155, ok := s2.(EIP155Signer)
return ok && eip155.chainId.Cmp(s.chainId) ==
} var big8 = big.NewInt() func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
if !tx.Protected() {
return HomesteadSigner{}.Sender(tx)
}
if tx.ChainId().Cmp(s.chainId) != {
return common.Address{}, ErrInvalidChainId
}
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
V.Sub(V, big8)
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
} // WithSignature returns a new transaction with the given signature. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
if err != nil {
return nil, nil, nil, err
}
if s.chainId.Sign() != {
V = big.NewInt(int64(sig[] + ))
V.Add(V, s.chainIdMul)
}
return R, S, V, nil
} // Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
s.chainId, uint(), uint(),
})
} // HomesteadTransaction implements TransactionInterface using the
// homestead rules.
type HomesteadSigner struct{ FrontierSigner } func (s HomesteadSigner) Equal(s2 Signer) bool {
_, ok := s2.(HomesteadSigner)
return ok
} // SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
return hs.FrontierSigner.SignatureValues(tx, sig)
} func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
} type FrontierSigner struct{} func (s FrontierSigner) Equal(s2 Signer) bool {
_, ok := s2.(FrontierSigner)
return ok
} // SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
if len(sig) != {
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
}
r = new(big.Int).SetBytes(sig[:])
s = new(big.Int).SetBytes(sig[:])
v = new(big.Int).SetBytes([]byte{sig[] + })
return r, s, v, nil
} // Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
})
} func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
} func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
if Vb.BitLen() > {
return common.Address{}, ErrInvalidSig
}
V := byte(Vb.Uint64() - )
if !crypto.ValidateSignatureValues(V, R, S, homestead) {
return common.Address{}, ErrInvalidSig
}
// encode the snature in uncompressed format
r, s := R.Bytes(), S.Bytes()
sig := make([]byte, )
copy(sig[-len(r):], r)
copy(sig[-len(s):], s)
sig[] = V
// recover the public key from the snature
pub, err := crypto.Ecrecover(sighash[:], sig)
if err != nil {
return common.Address{}, err
}
if len(pub) == || pub[] != {
return common.Address{}, errors.New("invalid public key")
}
var addr common.Address
copy(addr[:], crypto.Keccak256(pub[:])[:])
return addr, nil
}

通过上面的r,s,v得到的签名和信息hash来恢复公钥:

https://github.com/ethereum/go-ethereum/blob/master/crypto/signature_cgo.go

go-ethereum/crypto/signature_cgo.go

// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
return secp256k1.RecoverPubkey(hash, sig)
}

https://github.com/ethereum/go-ethereum/blob/master/crypto/secp256k1/secp256.go

go-ethereum/crypto/secp256k1/secp256.go

// RecoverPubkey returns the public key of the signer.
// msg must be the 32-byte hash of the message to be signed.
// sig must be a 65-byte compact ECDSA signature containing the recovery id as the last element.
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
if len(msg) != {
return nil, ErrInvalidMsgLen
}
if err := checkSignature(sig); err != nil {
return nil, err
} var (
pubkey = make([]byte, )
sigdata = (*C.uchar)(unsafe.Pointer(&sig[]))
msgdata = (*C.uchar)(unsafe.Pointer(&msg[]))
)
if C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[])), sigdata, msgdata) == {
return nil, ErrRecoverFailed
}
return pubkey, nil
}

https://github.com/ethereum/go-ethereum/blob/master/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h

go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h

int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) {
secp256k1_ge q;
secp256k1_scalar r, s;
secp256k1_scalar m;
int recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(pubkey != NULL); secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature);
VERIFY_CHECK(recid >= && recid < ); /* should have been caught in parse_compact */说明v可以为0,1,2,3
secp256k1_scalar_set_b32(&m, msg32, NULL);
if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) {
secp256k1_pubkey_save(pubkey, &q);
return ;
} else {
memset(pubkey, , sizeof(*pubkey));
return ;
}
}

同一个代码下

/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1
* sage: for t in xrange(1023, -1, -1):
* .. p = 2**256 - 2**32 - t
* .. if p.is_prime():
* .. print '%x'%p
* .. break
* 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'
* sage: a = 0
* sage: b = 7
* sage: F = FiniteField (p)
* sage: '%x' % (EllipticCurve ([F (a), F (b)]).order())
* 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
*/
static const secp256k1_fe_t secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST(//这是私钥的最大值
0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL,
0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL
); /** Difference between field and order, values 'p' and 'n' values defined in
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
* sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
* sage: a = 0
* sage: b = 7
* sage: F = FiniteField (p)
* sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order())
* '14551231950b75fc4402da1722fc9baee'
*/
static const secp256k1_fe_t secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST(
, , , , 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
);
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) {
unsigned char brx[];
secp256k1_fe fx;
secp256k1_ge x;
secp256k1_gej xj;
secp256k1_scalar rn, u1, u2;
secp256k1_gej qj;
int r; if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
return ;
} secp256k1_scalar_get_b32(brx, sigr);
r = secp256k1_fe_set_b32(&fx, brx);
(void)r;
VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */
if (recid & ) {//用于获得v二进制倒数第二位的值,即当v = 2或3时,即二进制倒数第二位为1,才进入该判断语句
if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= ) {
       /* fx + p >= n, so we can skip testing the second case. */
return ;
}
secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe);
}
//所以走到这的即v = 0或1的签名
if (!secp256k1_ge_set_xo_var(&x, &fx, recid & )) {
return ;
}
//前面这部分的内容真的很复杂,但是看起来像是进行一些检验
secp256k1_gej_set_ge(&xj, &x);
secp256k1_scalar_inverse_var(&rn, sigr);
secp256k1_scalar_mul(&u1, &rn, message);
secp256k1_scalar_negate(&u1, &u1);
secp256k1_scalar_mul(&u2, &rn, sigs);
secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1);
secp256k1_ge_set_gej_var(pubkey, &qj);
return !secp256k1_gej_is_infinity(&qj);
}

https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h

go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h

static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
int i;
#ifdef VERIFY
VERIFY_CHECK(a->normalized);
VERIFY_CHECK(b->normalized);
secp256k1_fe_verify(a);
secp256k1_fe_verify(b);
#endif
for (i = ; i >= ; i--) {
if (a->n[i] > b->n[i]) {
return ;
}
if (a->n[i] < b->n[i]) {
return -;
}
}
return ;
}
#ifdef VERIFY
static void secp256k1_fe_verify(const secp256k1_fe *a) {
const uint64_t *d = a->n;
int m = a->normalized ? : * a->magnitude, r = ;
/* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
r &= (d[] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[] <= 0x0FFFFFFFFFFFFULL * m);
r &= (a->magnitude >= );
r &= (a->magnitude <= );
if (a->normalized) {
r &= (a->magnitude <= );
if (r && (d[] == 0x0FFFFFFFFFFFFULL) && ((d[] & d[] & d[]) == 0xFFFFFFFFFFFFFULL)) {
r &= (d[] < 0xFFFFEFFFFFC2FULL);
}
}
VERIFY_CHECK(r == );
}
#endif
SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) {
#ifdef VERIFY
secp256k1_fe_verify(a);
#endif
r->n[] += a->n[];
r->n[] += a->n[];
r->n[] += a->n[];
r->n[] += a->n[];
r->n[] += a->n[];
#ifdef VERIFY
r->magnitude += a->magnitude;
r->normalized = ;
secp256k1_fe_verify(r);
#endif
}

https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/crypto/secp256k1/libsecp256k1/src/group_impl.h

go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h

static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
if (!secp256k1_ge_set_xquad(r, x)) {
return ;
}
secp256k1_fe_normalize_var(&r->y);
if (secp256k1_fe_is_odd(&r->y) != odd) {
secp256k1_fe_negate(&r->y, &r->y, );
}
return ; }

上面的代码太复杂,没看懂,但是大概觉得v的作用应该是用来对r的值进行的检验,以后在慢慢看吧!!!!!!!!!!!!!

ethereum/EIPs-155 Simple replay attack protection 35,36的更多相关文章

  1. Effective C++ 35,36,37

    35.使公有继承体现 "是一个" 的含义. 共同拥有继承意味着 "是一个".如  class B:public A. 说明类型B的每个对象都是一个类型A的对象, ...

  2. Replay attack 回放攻击

    w http://baike.baidu.com/item/重放攻击 重放攻击(Replay Attacks)又称重播攻击.回放攻击或新鲜性攻击(Freshness Attacks),是指攻击者发送一 ...

  3. EC读书笔记系列之16:条款35、36、37、38、39、40

    条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...

  4. [刷题]算法竞赛入门经典 3-4/UVa455 3-5/UVa227 3-6/UVa232

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 题目:算法竞赛入门经典 3-4/UVa455:Periodic Strings 代码: //UVa455 #inclu ...

  5. hdu5795 A Simple Nim 求nim求法,打表找sg值规律 给定n堆石子,每堆有若干石子,两个人轮流操作,每次操作可以选择任意一堆取走任意个石子(不可以为空) 或者选择一堆,把它分成三堆,每堆不为空。求先手必胜,还是后手必胜。

    /** 题目:A Simple Nim 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5795 题意:给定n堆石子,每堆有若干石子,两个人轮流操作,每次操作 ...

  6. Kotlin使用率达35%,Java要退位了?

    在今年的Google I/O大会上,关于Kotlin,Google只说了只言片语: 在过去一年里,有35%的专业Android开发者在使用Kotlin,其中95%的开发者都对Kotlin非常满意. 之 ...

  7. 【HDOJ5974】A Simple Math Problem(构造,解方程)

    题意:给定A与B,要求构造出一组X,Y,使得X+Y=A,lcm(X,Y)=B A<=2e4,B<=1e9 思路:A的范围较小,考虑以A为突破口 枚举A的约数k,复杂度O(sqrt(A)) ...

  8. HDU 5974 A Simple Math Problem ——(数论,大连区域赛)

    给大一的排位赛中数论的一题.好吧不会做...提供一个题解吧:http://blog.csdn.net/aozil_yang/article/details/53538854. 又学了一个新的公式..如 ...

  9. 从头到尾使用Geth的说明-3-geth参数说明和环境配置

    1.参数说明 ETHEREUM选项: --config value TOML 配置文件 --datadir "/home/user4/.ethereum" 数据库和keystore ...

随机推荐

  1. (转)分享一个技巧,利用批处理调用ruby脚本(可能你为路径苦恼)

    #关闭命令显示 @echo off #提示信息 echo Now,listing the controller,please not shutdown the DOS File! #切换到当前路径,. ...

  2. Django + Mysql 中关于时间异常返回500错误的解决

    问题描述: 最近在阿里云部署 Django(1.11.x) 时,在后台发布文章后,页面返回 500 异常. 刚开始的时候,遇到这个问题一脸懵逼,不知道该如何入手.后来把 settings.py 中 D ...

  3. 【读书笔记】iOS-音频设备访问

    音频的输入是通过麦克风实现,音频的输出是通过扬声气实现的.在iOS系统中无法直接操控麦克风和扬声器,苹果提供了丰富的音频API. 一,音频API介绍 在iOS和Mac OS X上开发音频应用,主要有两 ...

  4. 微软Azure AspNetCore微服务实战 第一期

    微服务大家已经不在陌生了,相对传统单体架构其带来了更大的灵活性与多方位的效率提升. 2017官方发布了EshopOnContainers的微服务项目,其结合了.Net Core.Azure.Docke ...

  5. Nginx入门到精通

    目录 入门篇 1.Nginx的介绍 2.Nginx的安装 3.Nginx的深入剖析 4.Nginx的虚拟主机 5.Nginx的常用功能 6.Nginx的日志剖析 7.Nginx的location剖析 ...

  6. 安卓开发_深入理解Activity和Fragment的关系

    Fragment(碎片)是必须嵌入在 Activity(活动) 中使用的.Fragment的生命周期随着Activity的生命周期的变化而变化 一.首先让我们看下Activity和Fragment的生 ...

  7. js调用android本地java代码

    js调用android本地java代码 当在Android上使用WebView控件开发一个Web应用时,可以创建一个通过Javascript调用Android端java代码的接口.也就是可以通过Jav ...

  8. JHipster技术简介

    本文简单介绍Jhipster是什么,为什么用Jhipster,怎么用Jhipster. WHAT - 技术栈 JHipster是什么 JHipster是一个开发平台,用于生成,开发,部署Spring ...

  9. 自己搭建anki同步服务器

    最近帮孩子找学习的软件,发现了anki 不过同步速度太慢,但发现可以自己搭建同步服务器 具体方法见https://github.com/dsnopek/anki-sync-server 我的安装过程如 ...

  10. FastReport脚本把数据绑定到文本控件上

    public class ReportScript { private void Data25_BeforePrint(object sender, EventArgs e)//Data25是指需要绑 ...