Skip to content

Commit

Permalink
Limit the max height for some SYSCALLs (neo-project#1494)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang committed Mar 24, 2020
1 parent 76ccf21 commit d1b55dd
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 35 deletions.
75 changes: 44 additions & 31 deletions src/neo/SmartContract/InteropService.Blockchain.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.VM;
using Neo.VM.Types;
using System;
Expand All @@ -11,6 +12,8 @@ partial class InteropService
{
public static class Blockchain
{
public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement;

public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.None);
Expand All @@ -26,68 +29,72 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine)

private static bool Blockchain_GetBlock(ApplicationEngine engine)
{
ReadOnlySpan<byte> data = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
UInt256 hash;
if (data.Length <= 5)
hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data));
else if (data.Length == 32)
if (engine.TryPop(out uint height))
{
hash = Ledger.Blockchain.Singleton.GetBlockHash(height);
}
else if (engine.TryPop(out ReadOnlySpan<byte> data))
{
if (data.Length != 32) return false;
hash = new UInt256(data);
}
else
{
return false;

}
Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null;
if (block == null)
engine.CurrentContext.EvaluationStack.Push(StackItem.Null);
else
engine.CurrentContext.EvaluationStack.Push(block.ToStackItem(engine.ReferenceCounter));
if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null;
engine.Push(block?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null);
return true;
}

private static bool Blockchain_GetTransaction(ApplicationEngine engine)
{
ReadOnlySpan<byte> hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash));
if (tx == null)
engine.CurrentContext.EvaluationStack.Push(StackItem.Null);
else
engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter));
if (!engine.TryPop(out ReadOnlySpan<byte> hash)) return false;
TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash));
if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null;
engine.Push(state?.Transaction.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null);
return true;
}

private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine)
{
ReadOnlySpan<byte> hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash));
engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne);
if (!engine.TryPop(out ReadOnlySpan<byte> hash)) return false;
TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash));
if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null;
engine.Push(state?.BlockIndex ?? BigInteger.MinusOne);
return true;
}

private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine)
{
ReadOnlySpan<byte> data = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
UInt256 hash;
if (data.Length <= 5)
hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data));
else if (data.Length == 32)
if (engine.TryPop(out uint height))
{
hash = Ledger.Blockchain.Singleton.GetBlockHash(height);
}
else if (engine.TryPop(out ReadOnlySpan<byte> data))
{
if (data.Length != 32) return false;
hash = new UInt256(data);
}
else
{
return false;

}
TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null;
if (block == null)
if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null;
if (block is null)
{
engine.CurrentContext.EvaluationStack.Push(StackItem.Null);
engine.Push(StackItem.Null);
}
else
{
int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger();
if (!engine.TryPop(out int index)) return false;
if (index < 0 || index >= block.Hashes.Length - 1) return false;

Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]);
if (tx == null)
engine.CurrentContext.EvaluationStack.Push(StackItem.Null);
else
engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter));
engine.Push(tx?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null);
}
return true;
}
Expand All @@ -102,6 +109,12 @@ private static bool Blockchain_GetContract(ApplicationEngine engine)
engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter));
return true;
}

private static bool IsTraceableBlock(StoreView snapshot, uint index)
{
if (index > snapshot.Height) return false;
return index + MaxTraceableBlocks > snapshot.Height;
}
}
}
}
2 changes: 1 addition & 1 deletion src/neo/neo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
<PackageReference Include="Neo.VM" Version="3.0.0-CI00210" />
<PackageReference Include="Neo.VM" Version="3.0.0-CI00211" />
</ItemGroup>

</Project>
21 changes: 18 additions & 3 deletions tests/neo.UnitTests/SmartContract/UT_Syscalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void System_Blockchain_GetBlock()

var block = new Block()
{
Index = 1,
Index = 0,
Timestamp = 2,
Version = 3,
Witness = new Witness()
Expand Down Expand Up @@ -68,13 +68,27 @@ public void System_Blockchain_GetBlock()
Assert.AreEqual(1, engine.ResultStack.Count);
Assert.IsTrue(engine.ResultStack.Peek().IsNull);

// With block
// Not traceable block

var height = snapshot.BlockHashIndex.GetAndChange();
height.Index = block.Index + Transaction.MaxValidUntilBlockIncrement;

var blocks = snapshot.Blocks;
var txs = snapshot.Transactions;
blocks.Add(block.Hash, block.Trim());
txs.Add(tx.Hash, new TransactionState() { Transaction = tx, BlockIndex = block.Index, VMState = VMState.HALT });

engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
engine.LoadScript(script.ToArray());

Assert.AreEqual(engine.Execute(), VMState.HALT);
Assert.AreEqual(1, engine.ResultStack.Count);
Assert.IsTrue(engine.ResultStack.Peek().IsNull);

// With block

height.Index = block.Index;

script.EmitSysCall(InteropService.Json.Serialize);
engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
engine.LoadScript(script.ToArray());
Expand All @@ -83,10 +97,11 @@ public void System_Blockchain_GetBlock()
Assert.AreEqual(1, engine.ResultStack.Count);
Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray));
Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(),
"5b22556168352f4b6f446d39723064555950636353714346745a30594f726b583164646e7334366e676e3962383d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c312c224141414141414141414141414141414141414141414141414141413d222c315d");
"5b22366b4139757552614430373634585358466c706674686b436b5954702f6e34623878715057476c6a6659303d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c302c224141414141414141414141414141414141414141414141414141413d222c315d");
Assert.AreEqual(0, engine.ResultStack.Count);

// Clean

blocks.Delete(block.Hash);
txs.Delete(tx.Hash);
}
Expand Down

0 comments on commit d1b55dd

Please sign in to comment.